gsu.bydl.gsu.by/Images/_i4f/_diploms/KRAVCHENKO.docx · Web view5.3 По мере...

Post on 10-Jul-2020

3 views 0 download

Transcript of gsu.bydl.gsu.by/Images/_i4f/_diploms/KRAVCHENKO.docx · Web view5.3 По мере...

МИНИСТЕРСТВО ОБРАЗОВАНИЯ РЕСПУБЛИКИ БЕЛАРУСЬ

Учреждение образования«Гомельский государственный университет

имени Франциска Скорины»Факультет математики и технологий программирования

Кафедра математических проблем управления и информатики

Допущена к защитеЗав. кафедрой_______________ В.С.Смородин "____"________________20___г.

Автоматизация загрузки результатов Internet-олимпиад в систему DL

Дипломная работа

Исполнитель студент группы ПМ-51 __________________ Ю.М. Кравченко

Научный руководительассистент кафедры МПУиИ __________________ М.А. Писпанен Рецензент д.т.н., профессор кафедры ВМиП __________________ В.В.Можаровский

Гомель 2017

Содержание

Введение...................................................................................................................31. Детальная постановка задачи...........................................................................4

1.1 Технология загрузки результатов ВКОШП.............................................41.2 Технология загрузки результатов Белорусской республиканской олимпиады..........................................................................................................111.3 Постановка задачи автоматизации интернет-олимпиад.......................21

2. Программно-файловая структура..................................................................243. Алгоритмы.........................................................................................................30

3.1 Файлы JSP-страниц......................................................................................303.2 Классы и методы..........................................................................................32

4. Технология использования...............................................................................404.1 Технология автоматической загрузки результатов ВКОШП..................404.2 Технология загрузки результатов Белорусской республиканской олимпиады..........................................................................................................45

5. Результаты апробации....................................................................................555.1 Результаты апробации ВКОШП.................................................................555.2 Результаты апробации Республиканской олимпиады...........................59

Заключение............................................................................................................67Список литературы...............................................................................................68Приложение А Разработанный программный код.............................................69

2

Введение

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

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

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

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

Средой разработки приложения является интегрированная среда разработки программного обеспечения IntelliJ IDEA, языком написания приложения - Java.

В ходе выполнения дипломной работы решаются следующие задачи: изучение базы данных MS SQL, используемой приложением; изучение и загрузка результатов олимпиад в ручном режиме; изучение каркасов разработки JSP, ApacheVelocity, JiXB, JDBC; автоматизация загрузки результатов Всероссийской олимпиады

школьников по программированию (ВКОШП) для сайта dl.gsu.by; загрузка результатов ВКОШП за 2015 и 2016 года; автоматизация загрузки результатов Белорусской

республиканской олимпиады для dl.gsu.by, загрузка результатов; реализация компонентов автоматизации.Актуальность данной темы обусловлена стремлением

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

Основной функцией программного компонента будет являться автоматизация загрузки результатов интернет-олимпиад для сайта dl.gsu.by

Данная дипломная работа состоит из 5 частей.В первой главе части дипломной работы описывается детальная

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

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

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

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

3

4

1. Детальная постановка задачи

1.1 Технология загрузки результатов ВКОШП

ВКОШП – это Всероссийская командная олимпиада школьников по программированию. Олимпиада проводится в один компьютерный тур. На туре команде предоставляется персональный компьютер и предлагается решить несколько задач. В олимпиаде принимают участие команды, каждая из которых составлена из трех школьников. Выше оказывается классифицирована команда, решившая большее количество задач. При равенстве количества решенных задач выше оказывается классифицирована команда, у которой меньше суммарное штрафное время. Каждый год выбирается количество команд, которые награждаются дипломами 1, 2 и 3 степени. Так, например, в 2015 году команды, решившие 6 задач, награждались дипломами 3 степени, команды, решившие 7 задач, награждались дипломами 2 степени, а команды, решившие 8 и более задач, награждаются дипломами 1 степени.

Для просмотра составов команд необходимо зайти по ссылкам как показано на рисунке 1.

Рисунок 1– Просмотр составов команд

Если зайти по данной ссылке, то можно просмотреть составы команд за разные годы (рисунок 2).

5

Рисунок 2 – Составы гомельских команд по годам

Процесс загрузки рассмотрен по шагам на примере “Десятой Всероссийской командной олимпиады школьников по программированию”.

1. Перед началом загрузки сделайте пожалуйста backup базы ROlimp (SQL Server).

2. Подключаемся к базе ROlimp.В случае возникновения проблем с 1 и 2 пунктами обратитесь на сайт

Microsoft в раздел базы данных или же перейдите по ссылке: https://msdn.microsoft.com/ru-ru/library/ms187510(v=sql.110).aspx где подробно разъяснено, как и что делать.

3. Создаем курс для олимпиады в базе ROlimp, таблице Courses:INSERT INTO Courses(Name, EName, ShortName, AwardType,

Active, Type, Accept, TimeBegin, TimeEnd, DefaultTaskHiLevel, DefaultTheoryLevel, RealtimeResults, PublicDetails, TableType, ParentID, Num, TasksViewType, CanGetSolution, Hidden, CanGetTest) VALUES(

'Деcятая Всероссийская командная олимпиада школьников по программированию', 'Russia 2009', 'C09', 1, 1, 3, 3, '2009-11-23', '2009-12-02', 10000, 10000, 1, 1, 5, 26, 1,1,1, 0, 0)

ParentID – 26 для данного типа соревнования.

6

Запрос стандартный кроме указания Name, TimeBegin, TimeEnd соответствующих вашей олимпиаде.

Необходимо знать ID (который был сгенерирован SQL Server автоматически) с которым был создан данный курс, т.к. он понадобится в дальнейшем. В данном случае ID = 75.

4. Данные из оригинальной таблицы результатов (в данном случае original\Standings.mht) необходимо представить в определенном формате и сохранить в текстовый файл. Более того, придется найти все гомельские команды и дать им такие имена, которые они имеют в базе ROlimp. В данном случае это команды:

Гомель, сборная #1Гомель, сборная #3Гомель, сборная #2Имена должны быть измены как показано ниже:Гомель, ГОМЕЛЬ-1Гомель, ГОМЕЛЬ-3Гомель, ГОМЕЛЬ-2В данном случае получился следующий файл: rus2009.txt.5. Необходимо конвертировать данный текстовый файл в XML

версию, которая используется для загрузки результатов в базу. Для этого:5.1 Запускаем программу (r-import\trunk\dev\src\TextToXML2.java),

которая преобразовывает текстовый формат в XML представление, передавая ей полный путь к текстовому файлу (в данном случае D:\Projects\DL\src\svn\r-import\trunk\dev\stuff\rus2009.txt).

5.2 После выполнения в той же директории будет создан XML файл (в данном случае rus2009.xml).

5.3 По мере выполнения программы, она выводит предупреждения в консоль. Так если в консоли присутствуют сообщения Fix(1), это значит, что города, указанные в текстовом файле, отсутствуют в базе ROlimp. В данном случае таких сообщений было несколько. Каждое из них говорит, какой SQL запрос может исправить проблему. Итак, необходимо запустить следующий SQL запросы на базе ROlimp (смотреть файл files\rus2009fixCities_draft.sql в приложении).

5.4 Страны, к которым относятся неопознанные города (можно поискать в google), придется определить самостоятельно. Когда страны будут определены, необходимо проверить есть ли они в таблице Counties. Если да, то на месте xxx в SQL скрипт нужно подставить ID страны в таблице Countries. Если страны нет, то требуется добавить её самостоятельно и узнав ID подставить его в запрос. В данном случае SQL скрипт принял вид (files/rus2009fixCities_final.sql). Запустите скрипт, что бы города были добавлены в базу.

5.5 Повторите пункт 5 еще раз.5.6 Подготавливаем rus2009.xml к import-у в ROlimp.5.6.1 Устанавливаем courseID в ID нашего курса (пункт 3):

<competitionInfo7

name="Россия 2009" contestType="TEAM" courseID="75" openDate="23.11.2009" collapseDate="02.12.2009" language="rus" > <header> <task name="A" /> <task name="B" /> <task name="C" /> <task name="D" /> <task name="E" /> <task name="F" /> <task name="G" /> <task name="H" /> <task name="I" /> <task name="J" /> <task name="K" /> </header></competitionInfo>

5.6.2 Необходимо найти все гомельские команды в xml файле и заменить:

Гомель, ГОМЕЛЬ-1Гомель, ГОМЕЛЬ-3Гомель, ГОМЕЛЬ-2на простоГОМЕЛЬ-1ГОМЕЛЬ-3ГОМЕЛЬ-25.6.3 Также проставить информацию по дипломам для гомельских

команд (в данном случае ГОМЕЛЬ-1 – диплом 1-й степени, ГОМЕЛЬ-2 – диплом 3-й степени).

В итоге был получен конечный файл ( files\rus2009_final.xml).5.7 Импортируем rus2009_final.xml в ROlimp.5.7.1 Убедитесь, что сделан backup базы ROlimp.5.7.2 Необходимо найти класс LoadXMLtoDB.5.7.3 Убедитесь, что используется следующий способ создания

ResultXML ResultXML rXML = new ResultXML(ResultXML.SaveResultInDB);

5.7.4 При запуске import используется следующий метод:FileReader r = new FileReader("D:\\Projects\\DL\\src\\svn\\

r-import\\trunk\\dev\\xmls\\rus2009_final.xml"); rXML.parseStream(new InputSource(r));5.7.5 Запускается класс на выполнение.5.7.6 В консоль выводится информация по мере import-а данных.

Смотрите import_to_sqlserver\log.txt ( лог для данной олимпиады, у вас, конечно, будет отличаться).

6. Нужно зайти на сайт как показано на рисунке 2.

8

7. Результаты данной олимпиады отображены на сайте (рисунок 3).

Рисунок 3 – Отображение команд после import-а XML

8. Составы команд взяты из прошлых годов, для занесения информации о командах за этот год, а также для указания актуальной информации по классам участников необходимо выполнить sql скрипты из files\fix_teams.sql. После выполнения составы команд обновятся (рисунок 4):

Рисунок 4 – Отображение команд после корректировки команд

9

9. Для данного соревнования отсутствует ссылка на таблицу результатов. Что бы её добавить:

9.1 Выполните запрос на базе данных ROlimp (это в данном случае):INSERT INTO resulttables ( CourseID, Title, ETitle, URL, EURL ) VALUES ( 75, 'Девятая Всероссийская командная олимпиада

школьников по программированию', 'Russia 2009', 'xrestable.jsp?

hd=2&hid=643&pi=6&pi=7&u.c=75&lng=rus&u.a&c.l=4&c.n=2&c.s=2&c.s=7', 'xrestable.jsp?hd=2&hid=643&pi=6&pi=7&u.c=75&lng=eng&u.a&c.l=4&c.n=2&c.s=2&c.s=7');

CourseID – ID нашего курса (у вас свой).URL – корректная ссылка на результаты, где hid у вас такой, какой

первый HeaderItemID был сгенерирован при импорте данных в базу ROlimp из rus2009_final.xml – можно посмотреть в лог файле (см 5.7.6, но у каждого будет свое значение).

INSERT INTO dl2ResultsCell (HeaderItemID,UserID,Value,TypeID) VALUES (643,1543,9.0,1).

u.c – id нашего курса (данный 75, у вас свой).10. После выполнения скрипта обновите страницу составов команд

Ссылка на результаты добавлена. (рисунок 5).

Рисунок 5 - Ссылка на таблицу результатов10

11. Ссылка на дипломы по участникам должна работать корректно (рисунок 6).

Рисунок 6 – Ссылка на дипломы

12. Дипломы по школам так же распределены корректно (рисунок 7):

11

Рисунок 7 – Отображение дипломов по школам

13.Результаты загружены

1.2 Технология загрузки результатов Белорусской республиканской олимпиады

Для того, чтобы просмотреть уже закруженные результаты необходимо зайти по ссылке Олимпиады на сайте dl.gsu/by/olymp. Появится список уже загруженных олимпиад (рисунок 8).

12

Рисунок 8 - Список республиканских олимпиад

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

Рисунок 9 - Результаты проведения Белорусской республиканской олимпиады 2009 года и доступ к различным видам статистик

Переходить по ссылкам (Статистика по дипломам, Статистика за разные годы, и т.д.) дает возможность просмотреть различные виды отчетов о проведении олимпиад. Данные для формирования данных отчетов содержаться в двух базах MS Access базе (Olymp\mdb\book.mdb) и MS SQL Server базе ROlimp на DL_SERVER-е. Загрузка результатов очередной олимпиады сводится к загрузке данных в базы, указанные выше.

Процесс загрузки рассмотрен по шагам на примере вымышленной (в момент написания данного документа) олимпиаде “Белорусская республиканская олимпиада 2010 года”

13

1. Перед началом загрузки сделайте пожалуйста backup-ы баз, указанных выше.

2. Подключаемся к базе ROlimp.3. Создаем событие проведения олимпиады в базе ROlimp, таблице

Calendar:INSERT INTO Calendar ( ID, Year, Month, DayStart, DayEnd, NameID, Place, Contestant, URLResult ) VALUES ( 216, 2010, 3, '2010-03-24', '2010-03-29', 17, 'Гомель', 1, 'showRes.asp?id=216');ID – следующий максимальный id в таблице Calendar.NameID – 17 для данного типа соревнование.Place – место проведения соревнования.Contestant – 1 для данного типа соревнования.URLResult – должен быть showRes.asp?id=<id>, где <id> - такой же как

и ID.Остальные поля интуитивны и не требуют пояснения.4. Заходим по ссылке Олимпиады. Олимпиада появилась в списке

(рисунок 10):

Рисунок 10 – Отображение названия олимпиады в списке олимпиад

14

Если выполнить переход по ссылке, то произойдет ошибка отображения страницы из-за отсутствия данных по данной олимпиаде в базе MS Access book.mdb.

5. Загружаем результаты проведения нашей олимпиады в базу book.mdb.

5.1 Результаты представлены таблицей Respublica в базе book.mdb (рисунок 11):

Рисунок 11 – Таблица Respublica в базе book.mdb

5.2 В данном случае оригинальные результаты представлены в виде следующего xls файла (рисунок 12):

Рисунок 12 – Оригинальные результаты олимпиады

5.3 Проще всего создать новый лист в xls, преобразовать данные в формат таблицы Respublica и выполнить import данных в базу book.mdb.

5.4 Преобразовываем данные в соответствии с форматом таблицы Respublica (рисунок 13):

15

Рисунок 13 – Преобразованные данные

5.5 Поле Price означает вид награды (7 – Диплом 1-й степени, 8 – Диплом 2-й степени, 9 – Диплом 3-й степени, 10 – Похвальный лист, 12 – отсутствие награды).

5.6 Выполните корректировку из 9.3.2.5.7 Открываем book.mdb.5.8 Из всплывающего меню выбираем Import… (рисунок 14):

Рисунок 14 – Выбор команды Import

5.9 Выбираем наш xls файл с преобразованными данными (рисунок 15):

16

Рисунок 15 – Выбор файла с преобразованными данными

5.10 Выбираем нашу страницу, нажимаем Next (рисунок 16):

Рисунок 16 – Выбор команды Next

5.11 Далее по умолчанию (рисунок 17):

17

Рисунок 17 – Выбор команды по умолчанию

5.12 Импортируем данные в таблицу Respublica (рисунок 18):

Рисунок 18 – Импорт данных в таблицу Respublica

5.13 Завершаем Import.

18

5.14 Закрываем файл book.mdb (если не закрыть файл, результаты могут не отображаться из за совместного доступа к файлу).

6. Снова заходим по ссылке 2010 (Гомель).7. Отображаются результаты проведения данной олимпиады

(рисунок 19):

Рисунок 19 – Результаты проведения олимпиады

8. Если выполнять переход по ссылкам на различные виды отчетов (Статистика по дипломам, Статистика за разные годы, и т.д.), то некоторые из отчетов будут не доступны, а некоторые не будут содержать данных по данной олимпиаде. Причиной является отсутствие необходимых данных в базе ROlimp MS SQL Server-а.

9. Загружаем необходимые данные в ROlimp.9.1 Экспортируем данные по данной олимпиаде из book.mdb в xml

файл.9.1.1 Открываем book.mdb.9.1.2 Находим модуль Functions (рисунок 20):

Рисунок 20 – Модуль Functions

19

9.1.3 Заходим в него.9.1.4 Необходимо найти функцию work и меняем в ней файл в который

будет происходить export. В данном случае это будеn d:\bel (Set f = fs.OpenTextFile("d:\bel" & rYear & ".xml", 2, True, TristateFalse))

9.1.5 Создаем процедуру для запуска export-а:Sub export()Call work(2010)End Sub9.1.6 Запускаем процедуру (рисунок 21):

Рисунок 21 – Запуск процедуры

9.1.7 В данном случае был создан файл d:\bel2010.xml.9.2 Создаем курс для нашей олимпиады в базе ROlimp, таблица

Courses:INSERT INTO Courses (Name, AwardType, Active, Type,

Accept, TimeBegin, TimeEnd, DefaultTaskHiLevel, DefaultTheoryLevel, RealTimeResults, PublicDetails, TableType, ParentID, Num, TasksViewType, CanGetSolution, Hidden, CanGetTest) VALUES ('Республиканская олимпиада по информатике 2010', 1, 1, 2, 3, '2010-03-24', '2010-03-29', 10000, 10000, 1, 1, 5, 29, 1, 1, 1, 0, 0);

Запрос стандартный кроме указания Name, TimeBegin, TimeEnd соответствующих вашей олимпиаде.

Необходимо знать ID (который был сгенерирован SQL Server автоматически) с которым был создан данный курс, т.к. он понадобится в дальнейшем. В данном случае ID = 74.

9.3 Подготавливаем bel2010.xml к import-у в ROlimp.9.3.1 Устанавливаем courseID в ID нашего курса (см 9.2)<competitionInfo name="Республика 2010" contestType="USER" courseID="74" openDate="24.03.2010"

collapseDate="29.03.2010" language="rus" > <header> <day name="Day 1"> <task name="Task 1" /> <task name="Task 2" /> <task name="Task 3" /> </day> <day name="Day 2"> <task name="Task 4" />

20

<task name="Task 5" /> <task name="Task 6" /> </day></header></competitionInfo>9.3.2 Все region у всех contestant должны быть описаны в ROlimp

таблица Regions. Замечание: Если у вас в bel2010.xml г. Минск в атрибуте region с пробелом, то необходимо его убрать (лучше всего это выполнить на стадии подготовки xls в book.mdb, чтобы избежать лишней работы по корректировки данных). Тоже касается и других значений (данные должны полностью соответствовать таблице Regions).

9.4 Импортируем bel2010.xml в ROlimp.9.4.1 Необходимо убедиться, что сделан backup базы ROlimp.9.4.2 Проверяем параметры соединения с базой в olymp.db.DBUtil

(находится в модуле r-olimp) (username, password, jdbcConnection).9.4.3 Находим класс LoadXMLtoDB.9.4.4 Убедитесь, что используется следующий способ создания

ResultXML ResultXML rXML = new ResultXML(ResultXML.SaveResultInDB);9.4.5 При запуске export использовал следующий метод:FileReader r = new FileReader("D:\\Projects\\DL\\src\\svn\\

r-import\\trunk\\dev\\xmls\\bel2010.xml"); rXML.parseStream(new InputSource(r));9.4.6 Запускаем класс на выполнение.9.4.7 В консоль выводится информация по мере import-а данных.

Смотрите export_to_sqlserver\log.txt (данный лог, в ином случае, конечно, будет отличаться).

10. Теперь будут доступны все виды отчетов.11. Если перейти по отчету Все гомельчане (рисунок 22):

Рисунок 22 – Таблица результатов

то отобразится следующая страница (рисунок 23).

21

Рисунок 23 – Страница результатов всех гомельчан

Ссылка «Таблица результатов» для нашего соревнования будет не верной. Для обновления ссылки необходимо:

11.1 Выполнить запрос на базе данных ROlimp:INSERT INTO resulttables (CourseID, Title, ETitle, URL,

EURL) VALUES (74, 'Республика 2010','Rebublics contest 2010', 'xrestable.jsp?hd=2&hid=610&u.c=74&pi=11&pi=3&c.s=1&c.s=9&lng=rus', 'xrestable.jsp?hd=2&hid=610&u.c=74&pi=11&pi=3&c.s=1&c.s=9&lng=eng');

CourseID – ID нашего курса (у вас свой).URL – корректная ссылка на результаты, где hid у вас такой, какой

первый HeaderItemID был сгенерирован при импорте данных в базу ROlimp из bel2010.xml – можно посмотреть в лог файле (см 9.4.7, но у вас будет свое значение).

INSERT INTO dl2ResultsCell (HeaderItemID,UserID,Value,TypeID) VALUES (610,200034,600.0,1).

u.c – id нашего курса (мой 74, у вас свой).12. Если участник не пресутствет в базе ROlimp, то при загрузке XML

он создается автоматически. К сожалению школу участника узнать не удается и из-за отсутствия данной информации некоторые отчеты могут выглядеть неполными. Так, например, отчет “Статистика за разные годы”. С помощью SQL скрипта, приведенного в приложении А (vkoshp.sql) можно узнать у каких участников в каких курсах не заполнена информация об учебном заведении:

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

14. Загрузка закончена.

1.3 Постановка задачи автоматизации интернет-олимпиад

22

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

Олимпиада проводится в один компьютерный тур. На туре команде предоставляется персональный компьютер и предлагается решить несколько задач. В олимпиаде принимают участие команды, каждая из которых составлена из трех школьников. Выше оказывается классифицирована команда, решившая большее количество задач. При равенстве количества решенных задач выше оказывается классифицирована команда, у которой меньше суммарное штрафное время. Каждый год выбирается количество команд, которые награждаются дипломами 1, 2 и 3 степени. Так, например, в 2015 году команды, решившие 6 задач, награждались дипломами 3 степени, команды, решившие 7 задач, награждались дипломами 2 степени, а команды, решившие 8 и более задач, награждаются дипломами 1 степени.

Высокоуровневая схема работы компонента представлена на рисунке 24.

Рисунок 24 – Схема работы компонента

Оригинальные результаты проведения олимпиады находятся на сайте https://neerc.ifmo.ru/school/russia-team/. Исходные данные предоставлены в виде таблицы формата HTML находящейся по адресу: https://neerc.ifmo.ru/school/archive/2015-2016/ru-olymp-team-russia-2015-standings (для 2015 года проведения), где для каждого года в адресной строке меняется год на соответствующий. Так для 2016 года таблица будет находится по адресу: https://neerc.ifmo.ru/school/archive/2016-2017/ru-olymp-team-russia-2016-standings.html. Каждая таблица результатов содержит следующие данные: номер по списку команды, команду (в названии команды

23

указан город, название команды, состав участников), время решения задач, количество решенных задач.

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

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

24

2. Программно-файловая структура

Основная программно-файловая структура компонента может быть представлена с помощью следующей схемы (рисунок 25):

Рисунок 25 – Программно-файловая структура

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

Главная страница, с которой происходит загрузка олимпиад - olymp-res.jsp. На странице присутствует форма для загрузки данных в которой задается тип олимпиады, год проведения олимпиады, даты проведения, место проведения олимпиады (для республиканской олимпиады по программированию).

После заполнения формы и отправки данных происходит вызов класса LoadOlympResultsServlet в который эти данные передаются.

LoadOlympResultsServlet.java – это основной "маршрутизатор" внутри компонента. Он определяет, как поступать с полученными из формы со страницы olymp-res.jsp данными, вызывает методы из LoadOlympResultsService, в случае необходимости перенаправляет на страницы olymp-res-countries.jsp и olymp-res-diploms.jsp, позже дополняет данные полученной с этих страниц информацией.

LoadOlympResutsService.java – это файл, содержащий одноименный класс с основными методами загрузки. Создает объект класса VKOSHP, инициализирует его, загружает необходимую информацию с сайта с

25

результатами ВКОШП, обращается к TextToXML2.java для создания списка объектов класса ResultContainer.java. Вызывает LoadXMLToDB.java для загрузки XML-файла в БД. Для обращений к базе использует методы класса DBUtil.java

TextToXML2.java: преобразует полученную из LoadOlympResutsService строку в объект класса ResultsContainer, предоставляя ему XML-файл с результатами олимпиады, а также список городов, для которых не найдены страны в базе данных ROlimp.

ResultsContainer.java: хранит в себе финальный XML-файл с результатами олимпиады, а также списки: городов с ненайденными странами и команд которым нужно задать степень диплома.

VKOSHP.java: содержит основную информацию об олимпиаде ВКОШП, а также предоставляет парсер, который преобразует полученную по URL-адресу с сайта с результатами ВКОШП HTML-страницу в строку определенного формата.

LoadXMLToDb.java: осуществляет финальную загрузку XML-файла в базу данных, используя XML-парсер из класса ResultXML.

ResultXML.java: XML-парсер, загружающий в БД информацию о результатах олимпиады.

Оlymp-res-countries.jsp – это страница, на которой для каждого полученного из сервлета LoadOlympResultsServlet города создается поле для ввода страны, в которой этот город находится. Предоставляет ссылки на запрос в Google по имени города. После ввода данных, происходит их отправка обратно в LoadOlympResultsServlet.

Оlymp-res-diploms.jsp – это страница, где для каждой команды, полученной из сервлета LoadOlympResultsServlet, создается поле для ввода степени диплома, полученного этой командой. По умолчанию значения во всех полях 0. После ввода данных, происходит их отправка обратно в LoadOlympResultsServlet.

Оlymp-res-finished.jsp - окончательная страница загрузки. На нее происходит перенаправление после загрузки всех данных в базу. Содержит ссылку на таблицу результатов на сайте dl.gsu.by.

Оlymp-res-respublica.jsp – показывает информацию о загруженных данных. Предоставляет полученный id из таблицы Corses, также предоставляет поле для ввода HeaderItemId.

Все файлы можно найти в папке проекта dl-web.Далее подробно рассмотрим таблицы, используемые для хранения

результатов. Первым шагом в загрузке результатов является создание курса для олимпиады в базе ROlimp, таблица Courses. Структура таблицы приведена в таблице 1.

26

Таблица 1 – Структура таблицы Courses

Атрибут Тип Описание

ID числовой Уникальный идентификатор курсаName, EName символьный Название курсаShortName символьный Сокращенное название курсаActive логический Активен ли курс (скрывает курс для

всех см. поле Hidden)Type числовой Тип курса (1 - учебный, 2 - личное

соревнование или 3 - командное соревнование)

Accept числовой Доступен 1 - инд. Пользователям, 2 - командам или 3 - и тем и другим

TimeBegin дата/время Время закрытия курсаTimeEnd дата/время Время начала курсаDefaultTaskHiLevel числовой «Верхний» уровень доступа к

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

DefaultTheoryLevel, числовой Уровень доступа к дереву теории, устанавливаемый по умолчанию

TableType, числовой Тип таблицы 1, 2, 3 – старые типы таблиц 5 – таблица на java

ParentID числовой Курс - родитель (организация дерева)

Num, числовой Порядок сортировки (организация дерева)

CanGetSolution числовой Пользователь может брать свое решение обратно

Hidden логический Курс скрыт для не-редакторов

При загрузке республиканской олимпиады необходимо добавить записи в таблицу Calendar, где создавалось событие проведения олимпиады в базе ROlimp. Рассмотрим структуру таблицы Calendar (таблица 2)

Таблица 2 – Структура таблицы Calendar

Название поля Тип Назначение1 2 3

ID числовой Уникальный идентификатор ячейки

Year числовой Год проведения олимпиадыMonth числовой Месяц проведения олимпиады

27

Окончание таблицы 2

1 2 3DayStart дата/время Дата начала проведения соревнованийDayEnd дата/время Дата окончания соревнованийNameID числовой 17, для данного типа соревнование.Plase символьный место проведения соревнования.Contestant числовой 1, для данного типа соревнованияURLResult символьный Строка showRes.asp?id=<id>, где <id> - такой же

как и ID.

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

Таблица dl2ResultsCell.Данная таблица используется для хранения информации по

результатам соревнований. Каждая запись в таблице описывает одну клеточку таблицы результатов. Для указания, какие данные хранятся в данной клеточке (ячейке) используется поле "type" (таблица 3).

Таблица 3 – Таблица dl2ResultsCell

Название поля Тип Назначение

headerItemId числовой Код соревнования из таблицы  dl2ResultsHeaderItem

userId числовой Код участника соревнования (Pupils/Teams)

[value] вещественный Значение в ячейке результатаtypeId числовой Тип ячейки (dl2ResultsCellType)ts timestamp “Слепок” времени внесения результатов

Таблица dl2ResultsCellType.Справочник типов ячеек, которые используются для хранения

результатов (таблица 4).

Таблица 4 – Таблица dl2ResultsCellType.Название поля Тип Назначение

1 2 3ID числовой Уникальный идентификатор ячейки

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

28

Окончание таблицы 41 2 3

rusDescription символьный Комментарий к названию ячейки по-русскиengTitle символьный Аналогично rusTitleengDescription

символьный Комментарий к названии ячейки по-английски

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

descOrder логический Указывает на порядок сортировки по данному полю (прямой/обратный)

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

Таблица dl2ResultsHeaderItemMapping.Таблица используется для связывания между собой элементов шапки

соревнований из таблицы Courses. Значения полей openDate и coolapseDate должны совпадать с датами открытия закрытия курса из таблицы Courses или же, если есть ограничения на даты открытия (часть задач открывается в один день, часть в другой), тогда даты должны совпадать с датами из таблицы TasksHiLoDates. Структура таблицы представлена таблицей 5.

Таблица 5 – Таблица dl2ResultsHeaderItemMappingНазвание поля

Тип Назначение

headerItemId числовой Уникальный идентификатор элемента шапкиcourseId числовой Код соревнования, с которым связан элемент

шапкиnodeId числовой Код узла в дереве задач.openDate дата/время Дата начала соревнованияcollapseDate дата/время Дата завершения соревнования

Таблица dl2ResultsHeaderItemТаблица используется для хранения шапки таблиц результатов. Шапка

хранится в виде иерархической структуры. Структура таблицы представлена таблицей 6.

29

Таблица 6 – Таблица dl2ResultsHeaderItemНазвание поля

Тип Назначение

itemId числовой Уникальный идентификатор элемента шапкиparentItemId числовой Код элемента-родителяchildIndex числовой Номер элемента в порядке его отображения в

таблице результатовrusTitle символьны

йНазвание элемента шапки по-русски

engTitle символьный

Название элемента шапки по-английски

tmp числовой Значение до конца не выясненоtmpParent числовой Используется для отладкиdeep числовой Уровень вложенности элемента шапки

(глубина)tmp2 числовой Используется для отладкиRootItemID числовой Код корневого элемента в шапке

30

3. Алгоритмы

3.1 Файлы JSP-страниц

Главная страница, с которой происходит загрузка олимпиад - olymp-res.jsp. При обращении по этому адресу, на экран выводится форма, которую необходимо заполнить для загрузки олимпиад. Код страницы olymp-res.jsp приведен в приложении.

Данный код практически полностью статичен, помимо того, что строка для ввода места проведения олимпиады показывается и прячется в зависимости от выбранного пункта меню (ВКОШП или Республика), а также помимо того, что выпадающий список с годом проведения имеет максимальное значение равное текущему году, заданному на сервере.

После заполнения данных формы, необходимо нажать на "загрузить результаты олимпиады". После этого, идет обращение к сервлету LoadOlympResultsServlet, где в http-параметрах запроса передаются необходимые данные.

Далее, рассмотрим страницу olymp-res-countries.jsp.<body>Список городов, для которых не найдены страны:<form method='POST' action='load-olymp-results.jsp'> <div style="padding-top: 10px"> <span style="display: table-cell; width: 200px;

font-weight: bold">Город</span> <span style="display: table-cell; width: 200px;

font-weight: bold">Страна</span> </div> <c:forEach var="city" items="${countries}"

varStatus="loop"> <p style="display: table"> <a href="http://google.com/#q=$

{fn:replace(city,' ','+')}" target="_blank" title="Найти город в Google"><label

for="country_${loop.index} " style="di

splay: table-cell; width: 200px; cursor: pointer;">${city}</label></a>

<input name="country_${loop.index}" style="display: table-cell; width: 200px" type="text" required/>

</p> <input name="load_cities" type="hidden"

value="true"/> <input name="cities_size" type="hidden" value="$

{countriesSize}"/> </c:forEach><input type="submit" value="Загрузить

города"></form></body>Данная страница получает список городов, для которых не были

найдены страны в базе данных. Используя специальный тег c:forEach, 31

происходит итерация по списку переданных городов, где для каждого создается элемент, содержащий название города и ссылку для поиска его по названию в поисковой системе Google, а также поле для ввода страны, которой соответствует этот город. Также, на странице представлены дополнительные поле для ввода, невидимые пользователю. Они необходимы для корректной обработки результатов, полученных с этой страницы в сервлете LoadOlympResultsServlet. Для отправки данных предоставлено поле для ввода типа input, которое на интерфейсе представлено кнопкой, по нажатию на которую произойдет перенаправление пользователя на страницу ввода степеней дипломов для команд либо страницу окончания загрузки.

Рассмотрим страницу olymp-res-diploms.jsp, отвечающую за отображение списка команд, для которых необходимо задать степени дипломов.

<body><div style='padding-bottom: 20px;'> <a href="http://neerc.ifmo.ru/school/archive/${year}-$

{year + 1}/ru-olymp-team-russia-${year}-standings.html" target="_blank">Таблица результатов</a></div><form method='POST' action='load-olymp-results.jsp'> <div> <span style="display: table-cell; width: 350px;

font-weight: bold">Команда</span> <span style="display: table-cell; width: 260px;

font-weight: bold">Степень диплома (0 - нет диплома)</span> </div> <c:forEach var="team" items="${teams}" varStatus="loop"> <p style="display: table"> <label for="team_${loop.index} " style="display:

table-cell; width: 350px">${team}</label> <input name="team_${loop.index}" style="display:

table-cell; width: 50px" type="text" value="0"/> </p> <input name="load_diploms" type="hidden"

value="true"/> <input name="teams_size" type="hidden" value="$

{teamsSize}"/> </c:forEach> <input type="submit" value="Загрузить диплом для

команд"></form></body>В страницу передается список команд, степени дипломов которым

необходимо задать. Используя тег c:forEach, для каждой команды будет создан элемент с ее названием и соответствующий элемент для ввода степени диплома, в котором по умолчанию будет задано значение 0. Также, как и в случае со страницей загрузки стран для городов, на странице присутствуют скрытые поля, необходимые для корректной обработки данных формы

32

сервлетом. Отправка данных происходит по нажатию на кнопку «Загрузить диплом для команд», представленную элементом input с типом «submit».

Также на странице предоставлена ссылка на страницу результатов олимпиады (для удобства).

Наконец, рассмотрим страницу olymp-res-finished.jsp, представляющую собой страницу с информацией об окончании загрузки олимпиады.

<html><head> <title>Результаты загружены</title></head><body><div>Результаты олимпиады успешно загружены.</div><a href="/olymp/result/teamInter.asp"

target="_blank">Перейти на страницу олимпиад</a></body></html>Как можно видеть, страница полностью статична и предоставляет

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

3.2 Классы и методы

Основная работа с данными происходит в методе doPost класса LoadOlympResultsServlet. Рассмотрим его более детально. Сначала происходит проверка на то, не является ли полученный запрос – данными со страницы olymp-res-diploms.jsp, представляющие собой список степеней диплома для белорусских команд:

String loadDiploms = request.getParameter("load_diploms"); if (loadDiploms != null &&

Boolean.valueOf(loadDiploms)) { try { loadDiplomsAndResults(request, response); } catch (SQLException e) { throw new RuntimeException(e); } return; }Как можно видеть, в случае, когда данные действительно пришли из

olymp-res-diploms.jsp, вызывается метод loadDiplomsAndResults, куда передаются объекты, представляющий собой HTML-запрос и HTML-ответ.

Далее, если данные получены не из olymp-res-diploms.jsp, происходит проверка на то, получены ли они из olymp-res-countries.jsp. За это отвечает следующий участок кода:

String loadCities = request.getParameter("load_cities");33

if (loadCities != null && Boolean.valueOf(loadCities) && citiesWithNoCountries != null) {

try { loadCountriesForCities(request, response); } catch (SQLException e) { throw new RuntimeException(e); } return; }Как можно видеть, в случае, когда данные действительно пришли из

olymp-res-countries.jsp, вызывается метод loadCountriesForCities, куда передаются объекты, представляющий собой HTML-запрос и HTML-ответ.

Если же запрос был получен ни от olymp-res-diploms.jsp, ни от olymp-res-countries.jsp, значит необходимо вызвать логику обработки данных для запроса со страницы olymp-res.jsp:

startDate = request.getParameter("startDate"); endDate = request.getParameter("endDate"); int olympTypeInt =

Integer.valueOf(request.getParameter("ot")); int year =

Integer.valueOf(request.getParameter("y")); OlympType olympType = olympTypeInt == 0 ?

OlympType.VKOSHP : OlympType.undefined;String result; try { result =

LoadOlympResultsService.loadOlympResults(olympType, year); } catch (SQLException e) { throw new RuntimeException(e); } this.year = year; this.olympType = olympType;Здесь данные из объекта request, представляющего собой HTML-

запрос, преобразуются в соответствующие типы данных Java. Также определяется тип олимпиады, который вместе с годом и датами проведения заносится во внутренние переменные класса LoadOlympResultsServlet. Также, здесь происходит загрузка результатов олимпиады для определенного типа олимпиады и года путем вызова метода LoadOlympResultsService.loadOlympResults, результат которого заносится в переменную result и представляет собой строку определенного формата.

Далее происходят следующие преобразования:ResultContainer res =

LoadOlympResultsService.generateResult(result); List<String> countriesNotFound =

res.getCountriesNotFound(); if (countriesNotFound.isEmpty()) { if (res.getGomelTeamNames().isEmpty()) { createCourseAndFinish(res, new

HashMap<String, Integer>(), request, response); } else {

34

forwardToDiploms(res.getGomelTeamNames(), request, response);

} return; }

List<String> parsedResult = new ArrayList<String>();

for (String c : countriesNotFound) { parsedResult.add(c.split(",")[0]); } citiesWithNoCountries = parsedResult;

request.setAttribute("countries", parsedResult); request.setAttribute("countriesSize",

parsedResult.size());Изначально, происходит преобразование переменной типа results в

объект класса ResultContainer, который содержит XML-файл и список городов, страны у которых не были найдены в БД, а также список команд, для которых нужно задать степени дипломов, путем вызова метода LoadOlympResultsService.generateResult. Если список городов, страны для которых не были найдены пуст, как и список команд, для которых еще не были заданы дипломы, то произойдет окончательная загрузка результатов в БД и перенаправление на страницу olymp-res-finished.jsp. Если же список городов, страны для которых не были найдены пуст, но при этом список команд, для которых еще не были заданы степени дипломов не пуст, то произойдет перенаправление на страницу olymp-res-diploms.jsp, где в параметрах запроса будут указаны команды. Наконец, если список городов, для которых не были найдены страны не пуст, то произойдет перенаправление на страницу olymp-res-countries.jsp, где в параметрах запроса будет указан список этих городов.

Далее, рассмотрим класс LoadOlympResultsService, методы которого вызываются из класса LoadOlympResultsServlet, описанного выше.

35

private static final Map<OlympType, Olympiad> olymps = new HashMap<OlympType, Olympiad>();

private static Olympiad currentOlymp = null; static { olymps.put(OlympType.VKOSHP, new VKOSHP()); } public static String loadOlympResults(OlympType

olympType, int year) throws SQLException, IOException { currentOlymp = olymps.get(olympType); if (currentOlymp == null) { throw new RuntimeException("Olymp type not

found: " + olympType); } currentOlymp.init(year); Long courseId =

getCourseID(currentOlymp.getParentID(), year); if (courseId > -1) { currentOlymp.removeResults(courseId); } String url = currentOlymp.getResultsURL(); return

currentOlymp.parseResults(loadContentsFromURL(url)); }На данном участке кода можно видеть инициализацию статической

переменной olymps, представляющей собой отображение типа олимпиады на объект класса, соответствующего данной олимпиаде. Далее он инициализируется типом и объектом классов, представляющих собой ВКОШП.

Метод loadOlympResultsService принимает в качестве параметров тип и год проведения олимпиады. Используя тип, берется соответствующий ему объект класса из переменной olymps и результат заносится в переменную currentOlymp. Далее, происходит инициализация объекта олимпиады путем вызова у него метода init и передачей ему года проведения олимпиады. Затем происходит проверка на то, не существует ли в базе данных результатов олимпиады данного типа с таким же годом проведения. Если они существуют, то необходимо их перед загрузкой новых результатов удалить, что приводит к вызову метода removeResults. Для загрузки результатов, необходимо сначала получить соответствующий им URL, что осуществляется с помощью вызова метода getResultsURL у объекта олимпиады. После этого, у того же объекта олимпиады вызывается парсер результатов путем вызова метода parseResults, куда передается HTML-страница, полученная в результате вызова метода loadContentsFromURL класса LoadOlympResultsService, который в свою очередь получает URL-адрес необходимой страницы в качестве параметра.

36

public static void createCourseAndLoadResults(int year, String startDate, String endDate, String res, Map<String, Integer> teamNameToDiplom) throws Exception {

int courseID = createCourse( year, 3, currentOlymp.getTitle(), currentOlymp.getShortTitleEN(), currentOlymp.getFullStartDate(startDate), currentOlymp.getFullEndDate(endDate), currentOlymp.getParentID()); String newRes = res .replace("name=\"Set

name\"", "name=\"" + currentOlymp.getShortTitleRU() + "\"") .replace("courseID=\"XX\"",

"courseID=\"" + courseID + "\"") .replace("openDate=\"XX.XX.XXXX\"",

"openDate=\"" + startDate + "." + year + "\"") .replace("collapseDate=\"XX.XX.XXXX\

"", "collapseDate=\"" + endDate + "." + year + "\""); String finalRes = putDiplomasForTeams(newRes,

teamNameToDiplom); log.debug(finalRes); int hid = LoadXMLtoDB.loadResults(finalRes); loadTeamResults( hid, courseID, currentOlymp.getTitle(), currentOlymp.getShortTitleEN()); }Метод createCourseAndLoadResults класса LoadOlympResultsService

производит все основные действия по загрузке результатов олимпиады в базу данных. Сначала, он создает новую запись в таблице Courses, вызывая метод createCourse этого же класса. Далее, он подставляет в полученный XML-файл информацию о названии олимпиады, ее courseID (ID записи в таблице Courses), дату начала и окончания. После этого, дополняет информацию о степенях дипломах для команд и затем осуществляет загрузку данных о результатах олимпиады путем передачи XML-файла с результатами в метод LoadXMLToDb.loadResults. После этого, для того чтобы отображать ссылку на результаты на странице со списком олимпиад на сайте dl.gsu.by, вызывается метод loadTeamResults, производящий последние манипуляции с данными о результатах олимпиады в БД.

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

37

public static void loadMissingCountriesForCities(Map<String, String> citiesToCountries) {

List<Country> countries = Arrays.asList(Country.getAllCountries());

Map<String, Country> countryNameToCountry = new HashMap<String, Country>();

for (Country c : countries) { countryNameToCountry.put(c.getName(0).toLowerCase(),

c); } Map<String, Country> citiesToCountriesObject = new

HashMap<String, Country>(); for (Map.Entry<String, String> entry :

citiesToCountries.entrySet()) { citiesToCountriesObject.put(entry.getKey(),

countryNameToCountry.get(entry.getValue().toLowerCase())); } for (Map.Entry<String, Country> entry :

citiesToCountriesObject.entrySet()) { if (entry.getValue() != null) { City.registerCity(entry.getKey(), 0,

entry.getValue()); } } return; }В качестве параметра этот метод принимает отображение названия

города на название страны. В первую очередь, происходит загрузка из БД списка всех стран, где каждая страна представлена объектом класса Country. После этого, на основе вышеупомянутого списка, создается отображение названия страны на объект класса Country. Далее, на основе обоих отображений создается еще одно, которое отображает название города на соответствующую ему страну, представленную теперь объектом класса Country. Наконец, для каждого элемента этого отображения вызывается метод City.registerCity, в который передается название города и объект класса Country, представляющий соответствующую этому городу страну.

public static ResultContainer generateResult(String input) throws IOException {

try { return TextToXML2.getXMLAsString(input); } catch (ParserException e) { throw new IOException(e); } }Метод generateResult класса LoadOlympResultsService необходим для

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

38

Рассмотрим класс TextToXML2 и его методы getXMLAsString и buildXMLFromFile.

public static ResultContainer getXMLAsString(String txtContents) throws IOException, ParserException {

TextToXML2 converter = new TextToXML2(); return converter.buildXMLFromFile(txtContents); }Метод getXMLAsString создает объект класса TextToXML2 и вызывает

метод buildXMLFromFile, передавая туда переданную ему же строку.Метод buildXMLFromFile (см. Приложение А) изначально объявляет

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

После вышеуказанного, метод создаст и инициализирует объект класса Velocity из библиотеки Apache Velocity. В него передастся список результатов и затем вызовется метод merge, который создаст XML-файл, содержимое которого поместится в строку, представленную переменной sw. После всего, метод вернет объект класса ResultContainer, который будет содержать XML-файл, список городов, страны для которых не были найдены и список команд, для которых необходимо загрузить данные о степенях дипломов.

Рассмотрим класс LoadXMLToDB, производящий загрузку XML-файла в базу данных.

public class LoadXMLtoDB { public static int loadResults(String inputContents)

throws Exception { ResultXML rXML = new

ResultXML(ResultXML.SaveResultInDB); InputStream s = new BufferedInputStream(new

ByteArrayInputStream(inputContents.getBytes())); List<Long> result = rXML.parseAndGetHeaderIDS(new

InputSource(s)); Collections.sort(result); return result.get(0).intValue(); }}Как можно видеть, в классе представлен единственный статический

метод, который осуществляет загрузку результатов под названием loadResults. В этом методе создается объект класса ResultXML, которому передается специальный режим (в нашем случае он означает сохранение результатов в БД). После этого, переданная в метод строка преобразуется в

39

поток и передается методу parseAndGetHeaderIDS класса ResultXML, который производит обработку переданного XML и загрузку данных в БД и возвращает список HeaderItemID – идентификаторов записей, записанных в БД. После этого, данный список сортируется и возвращается его минимальное значение, преобразованное в тип данных int. Это необходимо для последующего корректного отображения ссылки на результаты проведения олимпиады.

40

4. Технология использования

4.1 Технология автоматической загрузки результатов ВКОШП

Для загрузки результатов ВКОШП пользователю необходимо перейти на страницу загрузки результатов олимпиад находящуюся по адресу http://dl.gsu.by/admin/olymp/rimport/olymp-res.jsp. (рисунок 26)

Рисунок 26 - Главная страница загрузки результатов

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

41

Рисунок 27 – Страница со списком городов, для которых не были найдены страны

Пользователю необходимо вручную ввести названия городов (регистр не важен). Для удобства поиска стран добавлен поиск в google. Для этого необходимо просто щелкнуть по городу и автоматически пользователь будет перенаправлен на страницу с результатами поиска. После ввода всех стран необходимо нажать «Загрузить города».

После загрузки стран всем белорусским командам необходимо проставить степени полученных ими дипломов. На странице необходимо проставить все дипломы вручную. Для удобства просмотра таблицы основных результатов на страницу добавлена ссылка на таблицу результатов. По умолчанию все степени дипломов равны 0. Диплом 1 степени – это цифра 1 в поле ввода, диплом второй степени - 2 и третья степень диплома – это, соответственно, цифра 3 в поле ввода. После ввода необходимых данных необходимо нажать на кнопку «Загрузить дипломы для команд». (рисунок 28)

42

Рисунок 28– Загрузка дипломов для команд

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

Рисунок 29– Страница для загрузки дипломов

После перехода на страницу результатов загрузки олимпиады появится следующее окно (рисунок 30):

Рисунок 30 – Страница с результатами загрузки

В некоторых случаях необходимо редактировать составы команд. Для этого необходимо перейти по ссылке «Редактирование команд» на главной странице (рисунок 31).

43

Рисунок 31 – Страница с ссылкой на редактирование команд

После перехода по ссылке на экране появится страница со списком всех имеющихся белорусских команд (рисунок 32).

Рисунок 32 – Страница со списком всех белорусских команд44

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

Рисунок 33 – Страница для редактирования команд

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

Рисунок 34– Страница с загруженными результатами

Загрузка результатов завершена.

45

4.2 Технология загрузки результатов Белорусской республиканской олимпиады

Для загрузки результатов Белорусской республиканской олимпиады по информатике, необходимо перейти на страницу http://dl.gsu.by/admin/olymp/rimport/olymp-res.jsp. Затем пользователю в выпадающем списке под названием "Выберите олимпиаду" необходимо выбрать пункт "Республика".

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

После ввода всех необходимых данных, нужно нажать на кнопку "Загрузить результаты олимпиады". (рисунок 35)

Рисунок 35 – Страница загрузки результатов

Далее, пользователь будет перенаправлен на страницу, где будет предоставлена информация о произведенных действиях и предоставлен ID записи, добавленной в таблицу Courses (Необходим для последующей загрузки). Страница представлена на рисунке 36.

Рисунок 36 – Страница olymp-res-respublica.jsp

46

Также, на этой странице предоставлено поле для ввода HeaderItemId из пункта 11.1 инструкции (см. технологию ручной загрузки), позволяющее загрузить последние необходимые данные для олимпиады в базу данных, после загрузки соответствующего XML-файла с помощью r-import. Для этого, необходимо выполнить следующие пункты инструкции:

Загрузить результаты проведения олимпиады в базу book.mdb.Результаты представлены таблицей Respublica в базе book.mdb

(рисунок 37).

Рисунок 37 – Таблица Respublica в базе book.mdb

В 2017 году оригинальные результаты представлены в виде следующего xls файла (рисунок 38).

Рисунок 38 – Отображение результатов Белорусской республиканской олимпиады по программированию – 2017

47

Проще всего создать новый лист в xls, преобразовать данные в формат таблицы Respublica и выполнить import данных в базу book.mdb.

Преобразовываем данные в соответствии с форматом таблицы Respublica. Поле Price означает вид награды (7 – Диплом 1-й степени, 8 – Диплом 2-й степени, 9 – Диплом 3-й степени, 10 – Похвальный лист, 12 – отсутствие награды).

Все region у всех contestant должны быть описаны в таблице Regions базы данных ROlimp. Если у вас г. Гомель в атрибуте region с пробелом, то необходимо его убрать (лучше всего это выполнить на стадии подготовки xls в book.mdb, чтобы избежать лишней работы по корректировки данных). Тоже касается и других значений (данные должны полностью соответствовать таблице Regions).

Далее открываем book.mdb. Из всплывающего меню выбираем Import… (рисунок 39).

Рисунок 39 – Выбор пункта меню

Выбираем наш xls файл с преобразованными данными. Далее выбираем нашу страницу, нажимаем Next (рисунок 40).

48

Рисунок 40 – Выбор страницы

Далее выбираем по умолчанию (рисунок 41).

Рисунок 41 – Выбор по умолчанию

Импортируем данные в таблицу Respublica (рисунок 42).

49

Рисунок 42 – Импорт данных

Завершаем Import.Закрываем файл book.mdb (если не закрыть файл, результаты могут не

отображаться из-за совместного доступа к файлу).Снова заходим по ссылке 2017 (Гомель).Отображаются результаты проведения нашей олимпиады (рисунок 43).

50

Рисунок 43 – Отображение результатов проведения олимпиады

Если выполнять переход по ссылкам на различные виды отчетов (Статистика по дипломам, Статистика за разные годы, и т.д.), то некоторые из отчетов будут не доступны, а некоторые не будут содержать данных по нашей олимпиаде. Причиной является отсутствие необходимых данных в базе Rollinp MS SQL Server-а.

Загружаем необходимые данные в ROlimp. Экспортируем данные по нашей олимпиаде из book.mdb в xml файл. Открываем book.mdb. Находим модуль Functions (рисунок 44).

51

Рисунок 44 – Модуль Functions

Заходим в него. Находим функцию work и меняем в ней файл в который будет происходить export. В моем случае это будеn d:\bel (Set f = fs.OpenTextFile("d:\bel" & rYear & ".xml", 2, True, TristateFalse))/

Создаем процедуру для запуска экспорта:Sub export()Call work(2017)End SubЗапускаем процедуру (рисунок 45).

Рисунок 45 – Запуск процедуры

В моем случае был создан файл d:\bel2017.xml.Курс для нашей олимпиады в базе ROlimp, таблице Courses уже создан

автоматически.Необходимо знать ID (который был сгенерирован SQL Server

автоматически) с которым был создан данный курс, т.к. он понадобится нам в дальнейшем. В моем случае ID = 74.

Подготавливаем bel2010.xml к import-у в ROlimp.

52

Устанавливаем courseID в ID нашего курса.<competitionInfo name="Республика 2010" contestType="USER" courseID="74" openDate="24.03.2010"

collapseDate="29.03.2010" language="rus" > <header> <day name="Day 1"> <task name="Task 1" /> <task name="Task 2" /> <task name="Task 3" /> </day> <day name="Day 2"> <task name="Task 4" /> <task name="Task 5" /> <task name="Task 6" /> </day></header></competitionInfo>Опять же, как сказано выше, все region у всех contestant должны быть

описаны в ROlimp таблица Regions. Замечание: Если у вас в bel2010.xml г. Минск в атрибуте region с пробелом, то необходимо его убрать (лучше всего это выполнить на стадии подготовки xls в book.mdb, чтобы избежать лишней работы по корректировки данных). Тоже касается и других значений (данные должны полностью соответствовать таблице Regions).

Импортируем bel2017.xml в ROlimp.Пожалуйста, убедитесь, что вы сделали backup базы ROlimp.Проверяем параметры соединения с базой в olymp.db.DBUtil

(находится в модуле r-olimp) (username, password, jdbcConnection).Находим класс LoadXMLtoDB.Пожалуйста, убедитесь, что используется следующий способ создания ResultXML ResultXML rXML = new

ResultXML(ResultXML.SaveResultInDB);При запуске export я использовала следующий метод:FileReader r = new FileReader("D:\\Projects\\DL\\src\\svn\\

r-import\\trunk\\dev\\xmls\\bel2010.xml"); rXML.parseStream(new InputSource(r));Далее запускаем класс на выполнение.В консоль выводится информация по мере, import-а данных. Смотрите

export_to_sqlserver\log.txt. Теперь будут доступны все виды отчетов.Если перейти по отчету все гомельчане, то появится следующая

статистика (рисунок 46).

53

Рисунок 46 – Отображение результатов всех гомельчан в 2017 году

Ссылка «Таблица результатов» для нашего соревнования будет не-верной. Для обновления ссылки необходимо ввести на странице olymp-res-respublica.jsp представленной на рисунке 47, в поле для ввода HeaderItemId значение, которое позволяет загрузить последние необходимые данные для олимпиады в базу данных, после загрузки соответствующего XML-файла с помощью r-import. HeaderItemID был сгенерирован при импорте данных в базу ROlimp из bel2017.xml. Его можно посмотреть в лог файле.

Рисунок 47 – Страница olymp-res-respublica.jsp

Если участник не присутствует в базе ROlimp, то при загрузке XML он создается автоматически. К сожалению школу участника узнать не удается и из-за отсутствия данной информации некоторые отчеты могут выглядеть неполными. Так, например, отчет “Статистика за разные годы” (рисунок 48).

54

Рисунок 48 – Отображение статистики за разные годы

С помощью следующего SQL скрипта можно узнать у каких участников в каких курсах не заполнена информация об учебном заведении:

select * from (SELECT HIM.CourseID,-1 TeamID,RC.UserID,RC.value,(

SELECT top 1 Name FROM UsersHistory WHERE date <= OpenDate and ID= RC.UserID ORDER BY date desc) as Name, ( SELECT top 1 City FROM UsersHistory WHERE date <= OpenDate and ID= RC.UserID ORDER BY date descas CityID, ( SELECT top 1 Region FROM UsersHistory WHERE date <= OpenDate and ID= UserID ORDER BY date desc) as RegionID, ( SELECT top 1 Country FROM UsersHistory WHERE date <= OpenDate and ID= RC.UserID ORDER BY date desc) as CountryID,( SELECT top 1 Form FROM UsersHistory WHERE date <= OpenDate and ID= RC.UserID ORDER BY date desc)as FormID, ( SELECT top 1 School FROM UsersHistory WHERE date <=

OpenDate and ID= RC.UserID ORDER BY date desc) as School, OpenDate FROM dl2resultscell RC INNER JOIN dl2resultsHeaderItemmapping HIM ON HIM.HeaderItemID= RC.HeaderItemID

WHERE typeid = 9 and userID> 200000) as xxx where school is null and regionid = 3 order by courseid desc

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

Загрузка закончена.55

5. Результаты апробации

5.1 Результаты апробации ВКОШП

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

Страница с результатами загрузки олимпиады, на которой представлен год загрузки и команды участники. Страница находится по адресу http://dl.gsu.by/olymp/result/teamInter.asp. Страница представлена на рисунке 49.

Рисунок 49 – Страница с результатами олимпиад56

Перейти на страницу с результатами всех команд можно по ссылке http://dl.gsu.by/olymp/xrestable.jsp?hd=2&hid=785&pi=6&pi=7&u.c=107&lng=rus&u.a&c.l=4&c.n=2&c.s=2&c.s=7.. На данной странице показано итоговое место команды в общем зачете, название команды, страна и город команды, время, затраченное на выполнение задач, количество решенных задач. Страница с результатами представлена на рисунке 50.

Рисунок 50 – Страница просмотра результатов всех команд-участников

Состав команды и ее результаты можно посмотреть просто нажав на нее. Страница для команды Гомель-1 находится по адресу http://dl.gsu.by/servlet/olympResultsShowUserRank?u.id=13&u.c=107&lng=rus На данной странице представлен состав команды, а также результаты участия команды в соревнованиях прошлых лет (рисунок 51).

57

Рисунок 51 – Просмотр состава и результата команды Гомель-1

Страница с результатами загрузки результатов ВКОШП-2015 находится по адресу http://dl.gsu.by/olymp/xrestable.jsp?hd=2&hid=773&pi=6&pi=7&u.c=106&lng=rus&u.a&c.l=4&c.n=2&c.s=2&c.s=7. Страница представлена на рисунке 52.

Рисунок 52 – Страница результатов ВКОШП-201558

Страница с результатами гомельчан на международных командных олимпиадах представлена по адресу http://dl.gsu.by/olymp/result/personalResult.asp. На странице отображено имя участника, количество заработанных им дипломов и года участия. Скриншот страницы приведен на рисунке 53.

Рисунок 53 – Результаты гомельчан в международных командных олимпиадах

Страница с участием учебных заведений в завоевании медалей и дипломов на международных соревнованиях. Ссылка на страницу: http://dl.gsu.by/olymp/result/SchoolInter.asp. Страница показана на рисунке 54.

59

Рисунок 54– Участие учебных заведений

5.2 Результаты апробации Республиканской олимпиады

Главная страница со списком всех загруженных олимпиад находится по адресу http://dl.gsu.by/olymp/result.asp. Скриншот страницы представлен на рисунке 55.

60

Рисунок 55 – Страница со списком всех олимпиад

Страница с результатами Белорусской республиканской олимпиады может быть получена по следующей ссылке http://dl.gsu.by/olymp/showRes.asp?id=231. Страница представлена на рисунке 56.

Рисунок 56 – Страница с результатами Белорусской республиканской олимпиады

61

Страница статистики по дипломам на республиканской олимпиаде доступна по ссылке http://dl.gsu.by/olymp/rdiplom.asp?ry=2017. Также на этой странице представлены участники, прогноз на следующий годи результаты школ в завоевании дипломов и похвальных листов.(рисунок 57)

Рисунок 57 – Страница статистики по дипломам

На странице http://dl.gsu.by/olymp/rgomel.asp представлены общие результаты участия областей (участник/диплом+похвальный лист), статистика по школам гомельской области, распределение дипломов по степеням (область можно выбрать), распределение дипломов по классам, а также 3 лучших места по классам и все места участников по гомельской области. (рисунок 58-63)

62

Рисунок 58 – Общие результаты (дипломы и похвальные листы)

Рисунок 59 – Статистика по гомельским школам

63

Рисунок 60 – Распределение дипломов по степеням

Рисунок 61– Страница с распределением дипломов по классам

64

Рисунок 62– Страница с лучшими местами по классам

Рисунок 63– Все места участников

Страница личных достижений гомельчан на республиканских олимпиадах находится по адресу http://dl.gsu.by/olymp/rpersonal.asp . На странице указано имя участника, школа, года участия в республиканской олимпиаде, результат (количество дипломов и отзывов). Скриншот страницы представлен на рисунке 64.

65

Рисунок 64 – Персональные достижения гомельчан

Страница результатов гомельчан на республиканских олимпиадах по годам находится по адресу http://dl.gsu.by/olymp/info/allgomel. На странице представлен список всех гомельчан, отсортированный по классам с результатом. Страница отображена на рисунке 65.

66

Рисунок 65 – Достижение гомельчан в 2017 году

Страница результатов по классам находится по адресу http://dl.gsu.by/olymp/showres.asp?id=231&nc=0. На данной странице отображено место команды в общем зачете, имя участника, команда, которую он представлял, количество решенных задач и полученных очков, а также полученная награда. Скриншот страницы представлен на рисунке 66.

Рисунок 66 – Статистика по классам

67

Заключение

В ходе дипломной работы были изучены необходимые теоретические вопросы, связанные с разработкой компонента для загрузки результатов интернет-олимпиад. Данный компонент реализован с помощью языка программирования Java и фреймворков JSP, Apache Velocity, JiXB, JDBC.

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

Средой разработки приложения являлась интегрированная среда разработки программного обеспечения IntelliJ IDEA, языком написания приложения - Java.

В ходе выполнения дипломной работы решились следующие задачи: изучение базы данных MS SQL, используемой приложением; изучение и загрузка результатов олимпиад в ручном режиме; изучение каркасов разработки JSP, ApacheVelocity, JiXB, JDBC; автоматизация загрузки результатов Всероссийской олимпиады

школьников по программированию (ВКОШП) для сайта dl.gsu.by; загрузка результатов ВКОШП за 2015 и 2016 года; автоматизация загрузки результатов республиканской олимпиады

для dl.gsu.by, загрузка результатов; реализация компонентов автоматизации. В итоге был разработан компонент, который представляет собой

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

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

68

Список литературы

1 Брайан Хоган, HTML5 и CSS3 Веб-разработка по стандартам нового поколения. ООО Издательство «Питер», 2012 – 272 c.

2 Колисниченко, Д. PHP и MySQL. Разработка Web-приложений / Д. Колисниченко; БХВ-Петербург, 2016 – 608 с.

3 Макфарланд, Д. Большая книга CSS3 / Д. Макфарланд; издательство «Питер», 2012 – 896 с.

4 Майкл Эферган Java: справочник .- QUE Corporation, 1997, Издательство "Питер Ком", 1998

5 Герберт Ш. Java. Полное руководство.8-е изд. - СПб.: Москва, 2012. - 1140 с.: ил. - Парал. тит. англ.

6 Монахов, В. Язык программирования Java и среда NetBeans. / В. Монахов - СПб.: БХВ - Петербург, 2011. - 704 с.

7 Блинов, И. Java. Промышленное программирование / И. Блинов, В. Романчик - Минск: УниверсалПресс, 2007. - 704с.

8 Эккель, Б. Философия Java. Библиотека программиста. / Б. Эккель - 4-е изд.,доп. - СПб.: Питер, 2009. - 640с.

9 Хабибулин, И. Самоучитель Java. / И.Ш. Хабибулин. - Спб.: БХВ-Петербург, 2001. - 464с

10 Изучение технологий Java [Электронный ресурс]. - Режим доступа: http://java-course.ru/student/book1/servlet/ - Дата доступа: 9.04.2017.

69

Приложение АРазработанный программный код

Jsp-страницыOlymp-res.jsp<%@ page contentType="text/html;charset=windows-1251" %><%@ page pageEncoding="CP1251" %><%@ taglib prefix="dl" uri='/WEB-INF/tld/dl.tld' %><%@ taglib uri="http://java.sun.com/jsp/jstl/core"

prefix="c" %><%@taglib prefix="fmt"

uri="http://java.sun.com/jsp/jstl/fmt" %>

<jsp:useBean id="now" class="java.util.Date"/><fmt:formatDate var="year" value="${now}" pattern="yyyy"/><html><head> <title>Distance Learning Belarus | Загрузка результатов

олимпиад</title> <meta http-equiv="Content-Type" content="text/html;

charset=windows-1251"></head><body><script> function showMe(e) { var element =

document.getElementById("placeSelection"); if(e.selectedIndex === 1) { element.style.display = "block"; } else { element.style.display = "none"; } }</script><form method='POST' action='load-olymp-results.jsp'> <div> <label> <span style="display: table-cell; width:

240px">Выберите олимпиаду:</span> <select onchange="showMe(this);" style="display:

table-cell; width: 200px" name="ot"> <option value="0">ВКОШП</option> <option value="1">Республика</option> </select> </label> </div> <div>&nbsp;</div> <div> <label> <span style="display: table-cell; width:

240px">Выберите год олимпиады:</span>

70

<select style="display: table-cell; width: 200px" name="y">

<c:forEach begin="2010" end="${year}" varStatus="loop">

<option value="${loop.index}" <c:if test="${year ==

loop.index}">selected</c:if>>${loop.index}</option> </c:forEach> </select> </label> </div> <div style="margin: 15px 0"> <label> <span style="display: table-cell; width:

240px">Введите дату начала олимпиады (дд.мм):</span> <input style="display: table-cell; width: 200px"

value ="12.12" type="text" name="startDate" required/> </label> </div> <div style="margin-bottom: 15px"> <label> <span style="display: table-cell; width:

240px">Введите дату окончания олимпиады (дд.мм):</span> <input style="display: table-cell; width: 200px"

value="13.12" type="text" name="endDate" required/> </label> </div> <div id="placeSelection" style="margin-bottom: 15px;

display: none;"> <label> <span style="display: table-cell; width:

240px">Введите место проведения олимпиады:</span> <input style="display: table-cell; width: 200px"

type="text" name="place"/> </label> </div> <input type="submit" value="Загрузить результаты

олимпиады"></form><div style="padding-top: 20px"> <a href="../teams/teamlist.jsp">Редактирование

команд</a></div></body></html>Olymp-res-respulica.jsp<%@ page contentType="text/html;charset=windows-1251" %><%@ page pageEncoding="CP1251" %><%@ taglib prefix="dl" uri='/WEB-INF/tld/dl.tld' %><%@ taglib uri="http://java.sun.com/jsp/jstl/core"

prefix="c" %><%@taglib prefix="fmt"

uri="http://java.sun.com/jsp/jstl/fmt" %>71

<html><head> <title>Distance Learning Belarus | Загрузка результатов

олимпиад</title> <meta http-equiv="Content-Type" content="text/html;

charset=windows-1251"></head><body><div>Загрузка результатов Республиканской олимпиады по

информатике ${year}:</div><div>Создана запись в таблице Calendar согласно пункта 3

инструкции.</div><div>Создана запись в таблице Courses согласно пункта 9.2

инструкции.</div><div>CourseID для использования в пункте 9.3.1: <b>$

{courseID}</b>.</div>

<div style="margin-top: 30px;">Выполните остальные пункты инструкции вплоть до 11.1. Чтобы выполнить его, введите полученный HeaderItemID в поле

ниже и нажмите "Загрузить".</div><form method='POST' action='load-olymp-results.jsp'> <label> <span style="display: table-cell; width:

240px">Введите HID:</span> <input style="display: table-cell; width: 200px"

type="text" name="hid"/> </label> <input type="hidden" value="${courseID}"

name="courseID"/> <input type="hidden" value="${year}" name="year"/> <input type="hidden" value="true"

name="load_respublica_hid"/> <input type="submit" value="Загрузить"></form></body></html>olymp-res-diploms.jsp<%@ page contentType="text/html;charset=UTF-8"

language="java" %><%@ page pageEncoding="CP1251" %><%@ taglib prefix="dl" uri='/WEB-INF/tld/dl.tld' %><%@ taglib uri="http://java.sun.com/jsp/jstl/core"

prefix="c" %><html><head> <title>Distance Learning Belarus | Загрузка результатов

олимпиад</title> <meta http-equiv="Content-Type" content="text/html;

charset=windows-1251"></head>

72

<body><div style='padding-bottom: 20px;'> <a href="http://neerc.ifmo.ru/school/archive/${year}-$

{year + 1}/ru-olymp-team-russia-${year}-standings.html" target="_blank">Таблица результатов</a></div><form method='POST' action='load-olymp-results.jsp'> <div> <span style="display: table-cell; width: 350px;

font-weight: bold">Команда</span> <span style="display: table-cell; width: 260px;

font-weight: bold">Степень диплома (0 - нет диплома)</span> </div> <c:forEach var="team" items="${teams}" varStatus="loop"> <p style="display: table"> <label for="team_${loop.index} " style="display:

table-cell; width: 350px">${team}</label> <input name="team_${loop.index}" style="display:

table-cell; width: 50px" type="text" value="0"/> </p> <input name="load_diploms" type="hidden"

value="true"/> <input name="teams_size" type="hidden" value="$

{teamsSize}"/> </c:forEach> <input type="submit" value="Загрузить диплом для

команд"></form></body></html>

olymp-res-countries.jsp<%@ page contentType="text/html;charset=windows-1251" %><%@ page pageEncoding="CP1251" %><%@ taglib prefix="dl" uri='/WEB-INF/tld/dl.tld' %><%@ taglib uri="http://java.sun.com/jsp/jstl/core"

prefix="c" %><%@ taglib prefix="fn"

uri="http://java.sun.com/jsp/jstl/functions" %>

<html><head> <title>Distance Learning Belarus | Загрузка результатов

олимпиад</title> <meta http-equiv="Content-Type" content="text/html;

charset=windows-1251"></head><body>Список городов, для которых не найдены страны:<form method='POST' action='load-olymp-results.jsp'> <div style="padding-top: 10px">

73

<span style="display: table-cell; width: 200px; font-weight: bold">Город</span>

<span style="display: table-cell; width: 200px; font-weight: bold">Страна</span>

</div> <c:forEach var="city" items="${countries}"

varStatus="loop"> <p style="display: table"> <a href="http://google.com/#q=$

{fn:replace(city,' ','+')}" target="_blank" title="Найти город в Google"><label

for="country_${loop.index} " style="di

splay: table-cell; width: 200px; cursor: pointer;">${city}</label></a>

<input name="country_${loop.index}" style="display: table-cell; width: 200px" type="text" required/>

</p> <input name="load_cities" type="hidden"

value="true"/> <input name="cities_size" type="hidden" value="$

{countriesSize}"/> </c:forEach> <input type="submit" value="Загрузить города"></form></body></html>olymp-res-finished.jsp<%@ page contentType="text/html;charset=UTF-8"

language="java" %><html><head> <title>Результаты загружены</title></head><body>

<div>Результаты олимпиады успешно загружены.</div>

<a href="/olymp/result/teamInter.asp" target="_blank">Перейти на страницу олимпиад</a>

</body></html>--Java-файлы--LoadOlympResultsServlet.javapublic class LoadOlympResultsServlet extends HttpServlet {

private static final String LOAD_COUNTRIES_PAGE = "olymp-res-countries.jsp";

private static final String LOAD_DIPLOMS_PAGE = "olymp-res-diploms.jsp";

private static final String FINISHED_PAGE = "olymp-res-finished.jsp";

74

private static final String LOAD_RESPUBLICA_PAGE = "olymp-res-respublica.jsp";

private List<String> citiesWithNoCountries = null; private int year = 0; private OlympType olympType = null; private String startDate = null; private String endDate = null; private List<String> teamsWithNoDiploms = null;

@Override protected void doPost(final HttpServletRequest request,

final HttpServletResponse response) throws ServletException, IOException {

String loadRespublicaHid = request.getParameter("load_respublica_hid");

if (loadRespublicaHid != null && Boolean.valueOf(loadRespublicaHid)) {

String hid = request.getParameter("hid"); String year = request.getParameter("year"); String courseID =

request.getParameter("courseID"); try { LoadOlympResultsService.insertIntoResultTabl

esForRespublica(Integer.valueOf(hid), Integer.valueOf(courseID), "Республика " + year, "Republics contest " + year);

} catch (SQLException e) { throw new RuntimeException(e); } request.getRequestDispatcher(FINISHED_PAGE).forw

ard(request, response); return; }

String loadDiploms = request.getParameter("load_diploms");

if (loadDiploms != null && Boolean.valueOf(loadDiploms)) {

try { loadDiplomsAndResults(request, response); } catch (SQLException e) { throw new RuntimeException(e); } return; }

String loadCities = request.getParameter("load_cities");

75

if (loadCities != null && Boolean.valueOf(loadCities) && citiesWithNoCountries != null) {

try { loadCountriesForCities(request, response); } catch (SQLException e) { throw new RuntimeException(e); } return; }

startDate = request.getParameter("startDate"); endDate = request.getParameter("endDate");

int olympTypeInt = Integer.valueOf(request.getParameter("ot"));

int year = Integer.valueOf(request.getParameter("y"));

OlympType olympType = olympTypeInt == 0 ? OlympType.VKOSHP : olympTypeInt == 1 ? OlympType.Respublica : OlympType.undefined;

if (olympType == OlympType.Respublica) { try { Long id =

LoadOlympResultsService.getMaxCalendarID() + 1; int month =

Integer.valueOf(startDate.split("\\.")[1]); int dayStart =

Integer.valueOf(startDate.split("\\.")[0]); int dayEnd =

Integer.valueOf(endDate.split("\\.")[0]); String place =

request.getParameter("place");

LoadOlympResultsService.insertIntoCalendar( id, year, month, year + "-" + month + "-" + dayStart, year + "-" + month + "-" + dayEnd, 17L, place, 1L); int courseID =

LoadOlympResultsService.createCourse( year, 2, "Республиканская олимпиада по

информатике " + year, "Republics contest " + year, year + "-" + month + "-" + dayStart, year + "-" + month + "-" + dayEnd,

76

29L); request.setAttribute("year", year); request.setAttribute("courseID", courseID); request.getRequestDispatcher(LOAD_RESPUBLICA

_PAGE).forward(request, response); } catch (SQLException e) { throw new RuntimeException(e); } } else { String result; try { result =

LoadOlympResultsService.loadOlympResults(olympType, year); } catch (SQLException e) { throw new RuntimeException(e); }

this.year = year; this.olympType = olympType;

ResultContainer res = LoadOlympResultsService.generateResult(result);

List<String> countriesNotFound = res.getCountriesNotFound();

if (countriesNotFound.isEmpty()) { if (res.getGomelTeamNames().isEmpty()) { createCourseAndFinish(res, new

HashMap<String, Integer>(), request, response); } else { forwardToDiploms(res.getGomelTeamNames()

, request, response); } return; }

List<String> parsedResult = new ArrayList<String>();

for (String c : countriesNotFound) { parsedResult.add(c.split(",")[0]); } citiesWithNoCountries = parsedResult;

request.setAttribute("countries", parsedResult); request.setAttribute("countriesSize",

parsedResult.size()); request.getRequestDispatcher(LOAD_COUNTRIES_PAGE

).forward(request, response); } }

private void loadCountriesForCities(HttpServletRequest request, final HttpServletResponse response)

77

throws ServletException, IOException, SQLException {

Map<String, String> cityToCountry = new HashMap<String, String>();

for (int i = 0; i < citiesWithNoCountries.size(); i++) {

cityToCountry.put(citiesWithNoCountries.get(i), request.getParameter("country_" + i));

} LoadOlympResultsService.loadMissingCountriesForCitie

s(cityToCountry); String result =

LoadOlympResultsService.loadOlympResults(olympType, year); ResultContainer res =

LoadOlympResultsService.generateResult(result); if (!res.getGomelTeamNames().isEmpty()) { forwardToDiploms(res.getGomelTeamNames(),

request, response); return; } createCourseAndFinish(res, new HashMap<String,

Integer>(), request, response); }

private void loadDiplomsAndResults(HttpServletRequest request, final HttpServletResponse response)

throws ServletException, IOException, SQLException {

Map<String, Integer> teamNameToDiplom = new HashMap<String, Integer>();

for (int i = 0; i < teamsWithNoDiploms.size(); i++) {

teamNameToDiplom.put(teamsWithNoDiploms.get(i), Integer.valueOf(request.getParameter("team_" + i)));

} String result =

LoadOlympResultsService.loadOlympResults(olympType, year); ResultContainer res =

LoadOlympResultsService.generateResult(result); createCourseAndFinish(res, teamNameToDiplom,

request, response); }

private void forwardToDiploms(List<String> gomelTeamNames, HttpServletRequest request, final HttpServletResponse response)

throws ServletException, IOException {

teamsWithNoDiploms = gomelTeamNames; request.setAttribute("year", year); request.setAttribute("teams", teamsWithNoDiploms);

78

request.setAttribute("teamsSize", teamsWithNoDiploms.size());

request.getRequestDispatcher(LOAD_DIPLOMS_PAGE).forward(request, response);

}

private void createCourseAndFinish(ResultContainer result, Map<String, Integer> teamNameToDiplom,

HttpServletRequest request, final HttpServletResponse response)

throws ServletException, IOException {

/* if (!result.getCountriesNotFound().isEmpty()) { throw new IllegalStateException("Found

unregistered countries or cities: " + Joiner.on(",

").join(result.getCountriesNotFound())); }*/ try { LoadOlympResultsService.createCourseAndLoadResul

ts( year, startDate, endDate,

result.getResult(), teamNameToDiplom); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new ServletException(e); } request.getRequestDispatcher(FINISHED_PAGE).forward(

request, response); }

}LoadOlympResultsService.javapackage olymp.rimport.service;

import olymp.City;import olymp.Country;import olymp.db.DBUtil;import olymp.rimport.enums.OlympType;import olymp.rimport.results.ParserException;import olymp.rimport.type.Olympiad;import olymp.rimport.type.VKOSHP;import olymp.rimport.xml.LoadXMLtoDB;import olymp.rimport.xml.ResultContainer;import olymp.rimport.xml.TextToXML2;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.util.HtmlUtils;

import java.io.BufferedReader;79

import java.io.IOException;import java.io.InputStreamReader;import java.net.URL;import java.net.URLConnection;import java.sql.ResultSet;import java.sql.SQLException;import java.util.*;

import static olymp.rimport.enums.OlympType.Respublica;

public class LoadOlympResultsService {

private static final Logger log = LoggerFactory.getLogger(LoadOlympResultsService.class);

private static final Map<OlympType, Olympiad> olymps = new HashMap<OlympType, Olympiad>();

private static Olympiad currentOlymp = null;

static { olymps.put(OlympType.VKOSHP, new VKOSHP()); }

public static String loadOlympResults(OlympType olympType, int year) throws SQLException, IOException {

currentOlymp = olymps.get(olympType); if (currentOlymp == null) { throw new RuntimeException("Olymp type not

found: " + olympType); } currentOlymp.init(year); Long courseId =

getCourseID(currentOlymp.getParentID(), year); if (courseId > -1) { currentOlymp.removeResults(courseId); } String url = currentOlymp.getResultsURL(); return

currentOlymp.parseResults(loadContentsFromURL(url)); }

public static Long getMaxCalendarID() throws SQLException {

DBUtil dbUtil = null; try { dbUtil = DBUtil.getInstance(); ResultSet rs = dbUtil.executeQuery("SELECT

TOP(1) ID FROM Calendar ORDER BY ID DESC"); if (rs.next()) { return rs.getLong(1); } return -1L; } finally {

80

if (dbUtil != null) { dbUtil.close(); } } }

public static void insertIntoCalendar(Long id, int year, int month, String dayStart, String dayEnd, Long NameID, String place, Long contestant) throws SQLException {

DBUtil dbUtil = null; try { dbUtil = DBUtil.getInstance(); String queryBuilder = "INSERT INTO Calendar (ID,

Year, Month, DayStart, DayEnd, NameID, Place, Contestant, URLResult) VALUES (" + id + ", " +

year + ", " + month + ", " + "'" + dayStart + "'" + ", " + "'" + dayEnd + "'" + ", " + NameID + ", " + "'" + place + "'" + ", " + contestant + ", " + "'showRes.asp?id=" + id + "'" + ");"; dbUtil.executeSqlUpdate(queryBuilder); } finally { if (dbUtil != null) { dbUtil.close(); } } }

private static Long getCourseID(Long parentID, int year) throws SQLException {

DBUtil dbUtil = null; try { dbUtil = DBUtil.getInstance(); ResultSet rs = dbUtil.executeQuery("SELECT ID

FROM Courses WHERE ParentID = " + parentID + " AND ShortName = 'C" + (year % 100) + "'");

if (rs.next()) { return rs.getLong(1); } return -1L; } finally { if (dbUtil != null) { dbUtil.close(); } } }

81

public static void createCourseAndLoadResults(int year, String startDate, String endDate, String res, Map<String, Integer> teamNameToDiplom) throws Exception {

int courseID = createCourse( year, 3, currentOlymp.getTitle(), currentOlymp.getShortTitleEN(), currentOlymp.getFullStartDate(startDate), currentOlymp.getFullEndDate(endDate), currentOlymp.getParentID()); String newRes = res .replace("name=\"Set name\"",

"name=\"" + currentOlymp.getShortTitleRU() + "\"") .replace("courseID=\"XX\"",

"courseID=\"" + courseID + "\"") .replace("openDate=\"XX.XX.XXXX\"",

"openDate=\"" + startDate + "." + year + "\"") .replace("collapseDate=\"XX.XX.XXXX\

"", "collapseDate=\"" + endDate + "." + year + "\""); String finalRes = putDiplomasForTeams(newRes,

teamNameToDiplom); int hid = LoadXMLtoDB.loadResults(finalRes); loadTeamResults( hid, courseID, currentOlymp.getTitle(), currentOlymp.getShortTitleEN()); }

private static String putDiplomasForTeams(String res, Map<String, Integer> teamNameToDiploma) {

String newRes = res; Set<Map.Entry<String, Integer>> entries =

teamNameToDiploma.entrySet(); for (Map.Entry<String, Integer> entry : entries) { if (entry.getValue() == 0) { continue; } int ind = newRes.indexOf(entry.getKey()); if (ind == -1) { continue; } int ind2 = newRes.indexOf("totalResult", ind); int end = newRes.indexOf("/>", ind2); String firstPart = newRes.substring(0, end); String secondPart = newRes.substring(end,

newRes.length()); newRes = firstPart + " diplom=\"" +

entry.getValue() + "\"" + secondPart; } return newRes;

82

}

public static ResultContainer generateResult(String input) throws IOException {

try { return TextToXML2.getXMLAsString(input); } catch (ParserException e) { throw new IOException(e); } }

private static String loadContentsFromURL(String url) throws IOException {

URL website = new URL(url); URLConnection connection = website.openConnection(); BufferedReader in = new BufferedReader(new

InputStreamReader(connection.getInputStream())); StringBuilder response; try { response = new StringBuilder(); String inputLine; while ((inputLine = in.readLine()) != null) { response.append(inputLine); } } finally { in.close(); } return HtmlUtils.htmlUnescape(response.toString()); }

public static int loadMissingCountriesForCities(Map<String, String> citiesToCountries) {

List<Country> countries = Arrays.asList(Country.getAllCountries());

Map<String, Country> countryNameToCountry = new HashMap<String, Country>();

for (Country c : countries) { countryNameToCountry.put(c.getName(0).toLowerCas

e(), c); } Map<String, Country> citiesToCountriesObject = new

HashMap<String, Country>(); for (Map.Entry<String, String> entry :

citiesToCountries.entrySet()) { citiesToCountriesObject.put(entry.getKey(),

countryNameToCountry.get(entry.getValue().toLowerCase())); } for (Map.Entry<String, Country> entry :

citiesToCountriesObject.entrySet()) { if (entry.getValue() != null) { City.registerCity(entry.getKey(), 0,

entry.getValue());83

} } return 0; }

public static int createCourse(int year, int type, String title, String shortTitle, String startDate, String endDate, Long parentID) throws SQLException {

DBUtil dbUtil = DBUtil.getInstance(); try { String sqlQuery = "INSERT INTO Courses(Name, EName,

ShortName, AwardType, Active, Type, Accept, TimeBegin, TimeEnd, DefaultTaskHiLevel, DefaultTheoryLevel, " +

"RealtimeResults, PublicDetails, TableType, ParentID, Num, TasksViewType, CanGetSolution, Hidden, CanGetTest) VALUES ('" + title + "', '" + shortTitle + "'," +

"'C" + String.valueOf(year).substring(2) + "',1,1," + type + ",3,'" + startDate + "','" + endDate + "',10000, 10000, 1,1,5," + parentID + "," +

"1,1,1,0,0)"; dbUtil.executeSqlUpdate(sqlQuery); ResultSet rs = dbUtil.executeQuery("SELECT

TOP(1) ID FROM Courses ORDER BY ID DESC"); rs.next(); return rs.getInt("ID"); } finally { dbUtil.close(); } }

private static int loadTeamResults(int hid, int courseID, String title, String shortTitle) throws SQLException {

DBUtil dbUtil = null; try { dbUtil = DBUtil.getInstance(); String sqlQuery = "INSERT INTO

resulttables(CourseID, Title, ETitle, URL, EURL) VALUES " + "(" + courseID + ", '" + title + "', '"

+ shortTitle + "','xrestable.jsp?hd=2&hid=" + hid + "&pi=6&pi=7&u.c=" + courseID + "&lng=rus&u.a&c.l=4&c.n=2&c.s=2&c.s=7', " +

"'xrestable.jsp?hd=2&hid=" + hid + "&pi=6&pi=7&u.c=" + courseID + "&lng=eng&u.a&c.l=4&c.n=2&c.s=2&c.s=7');";

dbUtil.executeSqlUpdate(sqlQuery); } finally { if (dbUtil != null) { dbUtil.close(); } }

84

return 0; }

public static void insertIntoResultTablesForRespublica(int hid, int courseID, String title, String ETitle) throws SQLException {

DBUtil dbUtil = null; try { dbUtil = DBUtil.getInstance(); String sqlQuery = "INSERT INTO

resulttables(CourseID, Title, ETitle, URL, EURL) VALUES (" + courseID + ", '" + title + "', '" +

ETitle + "', 'xrestable.jsp?hd=2&hid=" + hid + "&u.c=" + courseID + "&pi=11&pi=3&c.s=1&c.s=9&lng=rus', " +

"'xrestable.jsp?hd=2&hid=" + hid + "&u.c=" + courseID + "&pi=11&pi=3&c.s=1&c.s=9&lng=eng')";

dbUtil.executeSqlUpdate(sqlQuery); } finally { if (dbUtil != null) { dbUtil.close(); } } }}

TextToXML2.javapackage olymp.rimport.xml;

import olymp.City;import olymp.rimport.results.ParserException;import olymp.rimport.results.entity.ResultRecord;import olymp.rimport.results.parser.TeamResult;import org.apache.velocity.Template;import org.apache.velocity.VelocityContext;import org.apache.velocity.app.Velocity;import org.apache.velocity.runtime.RuntimeConstants;import

org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.context.ApplicationContext;import

org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.*;import java.util.ArrayList;import java.util.List;

public class TextToXML2 {

static {85

try { Velocity.setProperty(Velocity.COUNTER_INITIAL_VA

LUE, 0); Velocity.setProperty(Velocity.INPUT_ENCODING,

"Cp1251"); Velocity.setProperty(Velocity.OUTPUT_ENCODING,

"Cp1251"); Velocity.setProperty(RuntimeConstants.RESOURCE_L

OADER, "classpath"); Velocity.setProperty("classpath.resource.loader.

class", ClasspathResourceLoader.class.getName()); Velocity.init(); } catch (Exception e) { throw new IllegalStateException(e); } }

private static final Logger log = LoggerFactory.getLogger(TextToXML2.class);

private ApplicationContext context = new ClassPathXmlApplicationContext("olymp/rimport/r-import.xml");

public static ResultContainer getXMLAsString(String txtContents) throws IOException, ParserException {

TextToXML2 converter = new TextToXML2(); return converter.buildXMLFromFile(txtContents); }

private ResultContainer buildXMLFromFile(String inputFileContents) throws IOException, ParserException {

int gomelTeamCount = 0; ArrayList<ResultRecord> resultRecordList = new

ArrayList<ResultRecord>(100); TeamResult parser; BufferedReader fin = new BufferedReader(new InputStreamReader(new

ByteArrayInputStream(inputFileContents.getBytes())));

final String lang = "rus";

String taskName = fin.readLine(); String header = fin.readLine(); parser = (TeamResult) context.getBean("teamParser"); parser.init(header, taskName,

System.getProperty("lang", lang)); String str = fin.readLine();

while (str != null) { str = str.replaceAll("\\*", "

").replaceAll("\'", " ").replaceAll("\"", ""); parser.setData(str); resultRecordList.add(parser.getResultRecord());

86

str = fin.readLine(); }

List<String> countriesNotFound = new ArrayList<String>();

List<String> gomelTeamNames = new ArrayList<String>();

for (ResultRecord result : resultRecordList) { if (result.getCountry().trim().isEmpty()) { String name = result.getName(); if (name.contains("+")) { int langKey = 0; City c =

City.findByName(name.substring(0, name.indexOf('+')).trim(), langKey);

if (c == null) { countriesNotFound.add(name); } else { String cityName;

if (name.contains(",")) cityName = name.substring(0,

name.indexOf(',')); else cityName = name;

City.registerCity(cityName, langKey, c.getCountry());

result.setCountry(c.getCountry().getName(langKey));

} } else { countriesNotFound.add(result.getName()); } } if

(result.getName().toLowerCase().contains("гомель")) { gomelTeamCount++; result.setName("Гомель-" + gomelTeamCount); gomelTeamNames.add(result.getName()); } if

(result.getName().toLowerCase().contains("мозырь")) { gomelTeamNames.add(result.getName()); } }

/* if (!countriesNotFound.isEmpty()) { return new ResultContainer("",

countriesNotFound, gomelTeamNames);87

}*/

//Build XML with Velocity try { VelocityContext context = new VelocityContext(); context.put("rList", resultRecordList); context.put("taskNames", parser.getTaskNames()); String templateName = parser.getTemplateName();// InputStream input =

TeamResult.class.getResourceAsStream(templateName); Template template =

Velocity.getTemplate("olymp/rimport/results/parser/" + templateName);

StringWriter sw = new StringWriter();// Velocity.evaluate(context, sw, "", new

InputStreamReader(input)); template.merge(context, sw); String res = sw.toString(); log.debug(res); return new ResultContainer(res,

countriesNotFound, gomelTeamNames); } catch (Exception e) { throw new RuntimeException(e); } }

}ResultContainer.javapackage olymp.rimport.xml;

import java.util.List;

public class ResultContainer {

private String result; private List<String> countriesNotFound; private List<String> gomelTeamNames;

ResultContainer(String result, List<String> countriesNotFound, List<String> gomelTeamNames) {

this.result = result; this.countriesNotFound = countriesNotFound; this.gomelTeamNames = gomelTeamNames; }

public String getResult() { return result; }

public List<String> getCountriesNotFound() { return countriesNotFound; }

88

public List<String> getGomelTeamNames() { return gomelTeamNames; }}

LoadXMLToDb.javapublic class LoadXMLtoDB {

public static int loadResults(String inputContents) throws Exception {

ResultXML rXML = new ResultXML(ResultXML.SaveResultInDB);

InputStream s = new BufferedInputStream(new ByteArrayInputStream(inputContents.getBytes()));

List<Long> result = rXML.parseAndGetHeaderIDS(new InputSource(s));

Collections.sort(result); return result.get(0).intValue(); }}ResultXML.javapackage olymp.rimport.resultManager;

import olymp.Course;import olymp.Settings;import olymp.db.DBException;import olymp.users.NewUser;import olymp.users.User;import org.apache.log4j.Logger;import org.xml.sax.*;import org.xml.sax.helpers.DefaultHandler;import org.xml.sax.helpers.XMLReaderFactory;

import java.sql.SQLException;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;import java.util.HashMap;import java.util.List;

/** * Класс используется для загрузки результатов соревнования

из XML-файла * Находиться в стадии разработки =), но потестировать можно

=) */public class ResultXML extends DefaultHandler implements

ErrorHandler {

private static Logger logger = Logger.getLogger(ResultXML.class);

89

private XMLReader xr; private boolean parseHeader = false; private String tagValue = ""; private long userID; private String[] parametersName = null; // классы для работы с шапкой соревнования private ResMan rm = null; private ResultHeader resultHeader = null; private HeaderItem currentItem = null; // Параметры private boolean loadHeader; private boolean saveResults; private boolean dontSaveHeader; private boolean registerUser; private String lang = "eng"; private int langID; private int userType = NewUser.USER; private List<Long> headerItemIDS = new

ArrayList<Long>();

/** * Сохранять результаты в БД * TODO: thread-unsafe */ public static long SaveResultInDB = 2;

/** * Коснтруктор класса * * @param params параметры, с помощью которых определяем

как будет идти разбор данных * @throws SAXException исключение которое будет

возникать в случае невозможности загрузки класса "org.apache.xerces.parsers.SAXParser"

*/ public ResultXML(long params) throws SAXException { logger.info("Contructor ResultXML");

xr = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");

xr.setDTDHandler(this); xr.setContentHandler(this); xr.setErrorHandler(this); //

http://stackoverflow.com/questions/4726279/parsing-an-xml-file-with-a-dtd-schema-on-a-relative-path

xr.setEntityResolver(new org.hibernate.util.DTDEntityResolver());

parseParameters(params);90

}

private void parseParameters(long params) { /* Загружать шапку из БД */ long loadHeaderFromDB = 1; loadHeader = (params & loadHeaderFromDB) != 0; saveResults = (params & SaveResultInDB) != 0; /* Не сохранять шапку в БД - если нужно что-то отлаживать */ long dontSaveHeader1 = 4; dontSaveHeader = (params & dontSaveHeader1) != 0; /* не регистрировать пользователя */ long dontRegisterUser = 8; registerUser = (params & dontRegisterUser) == 0; }

public void startDocument() { logger.debug("Start document"); }

public void endDocument() { logger.debug("End document"); }

public void startElement(String uri, String name, String qName, Attributes atts)

{ String value; logger.debug("Start element: " + qName); tagValue = ""; if (name.equals("competition")) return; if (name.equals("header")) return; if (name.equals("competitionInfo")) { parseCompetitionInfo(atts); return; } if (parseHeader) { if ((name.equals("day")) ||

(name.equals("task"))) { HeaderItem tempItem; value = atts.getValue("name"); tempItem = new HeaderItem(value,

currentItem); currentItem.addChild(tempItem); if (name.equals("day")) { currentItem = tempItem; } return;

91

} } else { if (name.equals("day")) { parseAttributes(atts); return; } if (name.equals("task")) { parseAttributes(atts); return; } } if (name.equals("tableOfScore")) { prepareForTableOfScore(); return; } if (name.equals("recordScore")) { prepareForRecordScore(); return; } if (name.equals("totalResult")) { parseAttributes(atts); return; } if (name.equals("contestant")) { parseUserInfo(atts); } }

private void parseAttributes(Attributes atts) { logger.debug("Current item: " +

currentItem.getName()); boolean noSolve; int iCount = parametersName.length; HashMap<Integer, Float> results = new

HashMap<Integer, Float>(7); //Проверяем sum==-1? noSolve = atts.getValue("sum").equals("-1");

for (int i = 1; i < iCount && !noSolve; i++) { String value = atts.getValue(parametersName[i]); if (value != null) { Float fValue = new Float(value); results.put(i, fValue); } } // Если есть решение по задаче - то сохраняем if (!noSolve) { if (saveResults) { rm.addResult(userID, currentItem, results); headerItemIDS.add(currentItem.getHeaderID())

; } }

92

if (currentItem.haveChild()) currentItem = currentItem.getChildItem(0);

}

private void prepareForRecordScore() { currentItem = rm.getHeader().getRootItem(); }

private void prepareForTableOfScore() { if (saveResults) { if (!rm.getHeader().getRootItem().isSaveInDB())

throw new RuntimeException("header not saved"); } }

/** * Метод "разбирает" атрибуты для нового участника и

регистрирует его в БД * * @param atts атрибуты участника */ private void parseUserInfo(Attributes atts) { HashMap<String, String> userInfo = new

HashMap<String, String>(10); String[] supportFields; NewUser nu = new NewUser(lang, userType); // Получаем список полей, которые поддреживаются для

ползователя или команды supportFields = nu.getSupportedFieldNames(); // По имени поля получаем значение атрибута String value; if (userType == NewUser.TEAM) { value = atts.getValue("name"); userInfo.put("name", value); } for (String supportField : supportFields) { value =

atts.getValue(supportField.toLowerCase()); // Если данный атрибут есть вносим его в

пользовательску информацию if (value != null) userInfo.put(supportField, value); } nu.setUserData(userInfo); if (registerUser) { userID =

nu.Register(rm.getHeader().getOpenDate()); rm.subscribeUser(userID); logger.debug("Registered or found user: " +

userID); logger.debug(userInfo.toString()); } else userID = 200000;

93

}

private void prepareHeader(long CourseID, String CourseName, Date oDate, Date cDate) //throws SQLException

{ rm = new ResMan(CourseID, oDate, cDate, langID); resultHeader = rm.getHeader(); resultHeader.getRootItem().setName(CourseName); currentItem = resultHeader.getRootItem(); parametersName = resultHeader.getSupportedFields(); }

public void endElement(String uri, String name, String qName) {

logger.debug("Tag Value: " + tagValue); logger.debug("End element: " + qName); if (name.equals("competitionInfo")) { if (currentItem.getParent() == null) { if (loadHeader) { if (!resultHeader.loadFromDB()) { throw new

RuntimeException(this.getClass().getName() + ".endElement: Can\'t load header from database");

} currentItem =

resultHeader.getRootItem(); } else { resultHeader.setRootItem(currentItem); if (!dontSaveHeader) resultHeader.saveInDB(); } } else throw new RuntimeException("Ошибка при

разборе файла: не достигнут корневой элемент ");

logger.debug("\n" + currentItem.toString());

if (rm != null) rm.setHeader(resultHeader); parseHeader = false; return; } if (parseHeader) { if (name.equals("day")) currentItem =

currentItem.getParent(); } else { if ((name.equals("task")) ||

(name.equals("day"))) { if (currentItem.getNext() != null)

currentItem = currentItem.getNext(); else currentItem = currentItem.getParent(); } } }

94

/** * метод обрабатывает символы которые находятся между

тегами &lt;tag&gt;chars&lt;/tag&gt; * * @param ch символы между тегами * @param start где они начинаються * @param length и сколько их */ public void characters(char ch[], int start, int length)

{ for (int i = start; i < start + length; i++) { switch (ch[i]) { case '\\': tagValue = tagValue + "\\\\"; break; case '"': tagValue = tagValue + "\\\""; break; case '\n': tagValue = tagValue + "\\n"; break; case '\r': tagValue = tagValue + "\\r"; break; case '\t': tagValue = tagValue + "\\t"; break; default: tagValue = tagValue + ch[i]; break; } } }

public void fatalError(SAXParseException e) throws SAXException {

logger.fatal(e); super.error(e); }

public void error(SAXParseException e) throws SAXException {

logger.fatal(e); super.error(e); }

private void parseCompetitionInfo(Attributes atts) { String tmp; String oDateStr; String cDateStr; tmp = atts.getValue("language"); if (tmp != null) lang = tmp;

95

langID = (lang.equalsIgnoreCase("rus") ? Settings.LANG_RU : Settings.LANG_EN);

tmp = atts.getValue("contestType"); userType = tmp.equalsIgnoreCase("team") ?

NewUser.TEAM : NewUser.USER; long CourseID = 0; String courseIDStr = atts.getValue("courseID"); if (!courseIDStr.equals("") && !

courseIDStr.equalsIgnoreCase("XX")) { CourseID = Long.parseLong(courseIDStr); } String nameContest = atts.getValue("name"); SimpleDateFormat df = new

SimpleDateFormat("dd.MM.yyyy"); oDateStr = atts.getValue("openDate"); cDateStr = atts.getValue("collapseDate"); if (cDateStr == null) cDateStr = oDateStr; Date oDate = df.parse(oDateStr, new

java.text.ParsePosition(0)); Date cDate = df.parse(cDateStr, new

java.text.ParsePosition(0)); if (CourseID == 0) { logger.info("CourseID is 0 - Reqister new

course"); Course newCourse = new

Course(User.findByID(100145)); if (lang.equals("eng")) { newCourse.eName = nameContest; newCourse.name = ""; } else { newCourse.name = nameContest; newCourse.eName = ""; } newCourse.setBegin(oDate); newCourse.setEnd(cDate); newCourse.setType(userType == NewUser.TEAM ?

Course.TEAM_COMPETITION : Course.OWN_COMPETITION); newCourse.setAccept(3); newCourse.setHidden(false); newCourse.save(); CourseID = newCourse.ID; logger.info("New Course was created. ID:" +

CourseID); } prepareHeader(CourseID, nameContest, oDate, cDate); parseHeader = true; }

public List<Long> parseAndGetHeaderIDS(InputSource src) {

try { xr.parse(src); } catch (SAXParseException e) {

96

/* The processing instruction target matching "[xX]

[mM][lL]" is not allowed Вылетает после того как результаты загружены,

поэтому только логируется.*/// logger.warn("", e); throw new RuntimeException(e); } catch (Exception e) { throw new RuntimeException(e); } return headerItemIDS; }}VKOSHP.javapackage olymp.rimport.type;

import olymp.db.DBUtil;import olymp.rimport.parser.OlympResultParser;import olymp.rimport.parser.VKOSHPResultParser;

import java.sql.ResultSet;import java.sql.SQLException;import java.util.*;

public class VKOSHP implements Olympiad {

private static final Map<Integer, String> VKOSHPYearToNumber = new HashMap<Integer, String>();

private static final Long parentID = 26L; private OlympResultParser parser = null; private int year;

static { VKOSHPYearToNumber.put(2012, "Тринадцатая"); VKOSHPYearToNumber.put(2013, "Четырнадцатая"); VKOSHPYearToNumber.put(2014, "Пятнадцатая"); VKOSHPYearToNumber.put(2015, "Шестнадцатая"); VKOSHPYearToNumber.put(2016, "Семнадцатая"); VKOSHPYearToNumber.put(2017, "Восемнадцатая"); VKOSHPYearToNumber.put(2018, "Девятнадцатая"); VKOSHPYearToNumber.put(2019, "Двадцатая"); VKOSHPYearToNumber.put(2020, "Двадцать первая"); VKOSHPYearToNumber.put(2021, "Двадцать вторая"); VKOSHPYearToNumber.put(2022, "Двадцать третья"); VKOSHPYearToNumber.put(2023, "Двадцать четвертая"); VKOSHPYearToNumber.put(2024, "Двадцать пятая"); VKOSHPYearToNumber.put(2025, "Двадцать шестая"); VKOSHPYearToNumber.put(2026, "Двадцать седьмая"); VKOSHPYearToNumber.put(2027, "Двадцать восьмая"); VKOSHPYearToNumber.put(2028, "Двадцать девятая"); VKOSHPYearToNumber.put(2029, "Тридцатая"); VKOSHPYearToNumber.put(2030, "Тридцать первая");

97

}

public VKOSHP() { }

@Override public void init(int year) { this.year = year; this.parser = new VKOSHPResultParser(); }

@Override public String parseResults(String results) { return this.parser.parseResults(results); }

@Override public String getTitle() { return VKOSHPYearToNumber.get(year) + "

Всероссийская командная олимпиада школьников по программированию";

}

@Override public String getShortTitleEN() { return "Russia " + year; }

@Override public String getShortTitleRU() { return "Россия " + year; }

@Override public String getFullStartDate(String startDate) { String[] startDayAndMonth = startDate.split("\\."); int startDay = Integer.valueOf(startDayAndMonth[0]); int startMonth =

Integer.valueOf(startDayAndMonth[1]); return year + "-" + startMonth + "-" + startDay; }

@Override public String getFullEndDate(String endDate) { String[] endDayAndMonth = endDate.split("\\."); int endDay = Integer.valueOf(endDayAndMonth[0]); int endMonth = Integer.valueOf(endDayAndMonth[1]); return year + "-" + endMonth + "-" + endDay; }

@Override public Long getParentID() { return parentID;

98

}

@Override public String getResultsURL() { return "http://neerc.ifmo.ru/school/archive/" + year + "-" + (year + 1) + "/ru-olymp-team-russia-" + year + "-standings.html"; }

@Override public void removeResults(Long courseId) throws

SQLException { List<Long> headerItemIds =

getHeaderItemIds(courseId); String hids = listToSQLString(headerItemIds); List<Long> userIds = getUserIds(hids); Date regDate = getRegDate(courseId); deleteCourseAndRelatedData(courseId, hids, userIds,

regDate); }

private String listToSQLString(List<Long> list) { StringBuilder hidsBuilder = new StringBuilder("("); for (Long id : list) { hidsBuilder.append(id).append(", "); }

hidsBuilder.delete(hidsBuilder.length() - 2, hidsBuilder.length() - 1);

hidsBuilder.append(")"); return hidsBuilder.toString(); }

private void deleteCourseAndRelatedData(Long courseId, String headerItemIds, List<Long> userIds, Date regDate) throws SQLException {

String startTransaction = "BEGIN TRANSACTION TRANS1";

String deleteFromResultTables = " DELETE FROM RESULTTABLES WHERE COURSEID = " + courseId;

String userIdsString = listToSQLString(userIds); String regDateString = DBUtil.wrapDate(regDate);

String beginTry = " BEGIN TRY"; String deleteFromDL2ResultTables = " DELETE FROM

dl2ResultsCell WHERE headerItemId IN " + headerItemIds;

99

String deleteFromDL2ResultsHeaderItemMapping = " DELETE FROM dl2ResultsHeaderItemMapping WHERE headerItemId IN " + headerItemIds;

String deleteFromDL2ResultsHeaderItem = " DELETE FROM dl2ResultsHeaderItem WHERE itemId IN " + headerItemIds;

String deleteFromUsersHistory = " DELETE FROM UsersHistory WHERE ID in " + userIdsString + " AND Date = " + regDateString;

String deleteFromUserCourses = " DELETE FROM UsersCourses WHERE CourseID = " + courseId;

String deleteFromCourses = " DELETE FROM Courses WHERE ID = " + courseId;

String deleteFromDL2ResultsIndex = " DELETE FROM dl2ResultsIndex WHERE CourseID = " + courseId;

String endTransaction = " COMMIT TRANSACTION TRANS1 END TRY BEGIN CATCH ROLLBACK TRANSACTION TRANS1 END CATCH";

String sqlDeleteQuery = startTransaction + beginTry + deleteFromResultTables + deleteFromDL2ResultTables + deleteFromDL2ResultsHeaderItemMappin

g + deleteFromDL2ResultsHeaderItem + deleteFromUsersHistory + deleteFromUserCourses + deleteFromCourses + deleteFromDL2ResultsIndex + endTransaction;

DBUtil dbUtil = null; try { dbUtil = DBUtil.getInstance(); dbUtil.executeSqlUpdate(sqlDeleteQuery); } finally { if (dbUtil != null) { dbUtil.close(); } }

}

private List<Long> getHeaderItemIds(Long courseId) throws SQLException {

DBUtil dbUtil = null; try { dbUtil = DBUtil.getInstance(); String query = "SELECT headerItemId FROM

dl2ResultsHeaderItemMapping WHERE courseId = " + courseId; ResultSet rs = dbUtil.executeQuery(query); List<Long> ids = new ArrayList<Long>();

100

while (rs.next()) { ids.add(rs.getLong(1)); } return ids; } finally { if (dbUtil != null) { dbUtil.close(); } } }

private List<Long> getUserIds(String headerItemIds) throws SQLException {

DBUtil dbUtil = null; try { dbUtil = DBUtil.getInstance(); String query = "SELECT userId FROM

dl2ResultsCell WHERE headerItemId IN " + headerItemIds; ResultSet rs = dbUtil.executeQuery(query); List<Long> ids = new ArrayList<Long>(); while (rs.next()) { ids.add(rs.getLong(1)); } return ids; } finally { if (dbUtil != null) { dbUtil.close(); } } }

private Date getRegDate(Long courseId) throws SQLException {

DBUtil dbUtil = null; try { dbUtil = DBUtil.getInstance(); String query = "SELECT TimeBegin FROM Courses

WHERE ID = " + courseId; ResultSet rs = dbUtil.executeQuery(query); if (rs.next()) { return getDateDayBefore(rs.getDate(1)); } return null; } finally { if (dbUtil != null) { dbUtil.close(); } } }

private Date getDateDayBefore(Date date) { Calendar newDate = Calendar.getInstance(); newDate.setTime(date);

101

newDate.add(Calendar.DATE, -1); return newDate.getTime(); }}

102

103