· Переход назад.
Иногда возникает необходимость вернуться назад при отладке. В данной среде достаточно лишь удалить контекст вызова функции, в результате чего произойдет переход назад по стеку вызовов функции. При этом глобальное состояние выполнения программы не откатится.[12]
· Переход к курсору.
Эта функция используется, если нужно возобновить выполнение программы и остановиться на какой-то другой строчке кода, не делая точку останова.[12]
· Добавление меток.
Используется для того, чтобы выделить цветом определенный элемент. Таким образом, во время отладки при появлении элемента в списке, вы сразу заметите его. [12]
· Вычисление выражений.
Во время отладки можно вычислить любое выражение. [12]
· Точки останова.
Помимо условных точек останова, существуют еще точки останова переменных (срабатывают при чтении или записи в какую-то переменную), точки останова по событиям (вычисляя определенное выражение в нужной строке кода, при этом выполнение не прерывается) и временные точки останова (сработают только один раз).[12]
· Замена во время режима отладки.
В результате того, что ВМ (Виртуальная машина) Java поддерживает HotSwap, существует возможность вносить изменения прямо в процессе отладки. При этом перезагружаются измененные классы.[12]
Словесно описать общий алгоритм программы можно следующим образом:
) Получение данных от пользователя.
) Проверка данных на корректность.
) Составление листа запросов.
) Произведение опроса нужных ресурсов согласно листу.) На каждом шаге - получение данных с требуемых ресурсов.) Обработка полученных данных.
) Составление рейтинга на основе всех результатов.
) Вывод результатов в отчет.
Первая стадия, это взаимодействие с пользователем - предоставление ему интерфейса для удобной работы с программой, реакция на его действия в интерфейсе (реакция на события), проверка введенных им данных и составление на их основе структур для составления в будущем запросов к ресурсам.
Важным в этой стадии является то, что в ней необходимо минимизировать вероятность занести ошибку пользователем. Поэтому разработаем алгоритм проверки корректности введенных данных:
) Проверка соответствия количества указанных баллов, количеству задач.
) Проверка наличия идентификаторов у инспектируемых студентов для заданных ресурсов.
) Проверка корректности ИД (только цифры).
) Проверка корректности номеров задач (только цифры и запятые).
) Проверка наличия временного интервала.
) Проверка наличия задач и их источников.
Если ПП обнаружит ошибку в данных, то выведет окно со списком найденных ошибок. Пока пользователь их не исправит, он не сможет получить отчет.
Второй стадией является составление списка запросов, которые ПП будет отправлять требуемым ресурсам:
) Составление общего списка задач и инспектируемых студентов.
) Создание данных для запроса (один запрос хранит в себе ИД, ресурс, номер требуемой задачи).
) Запись запроса в лист запросов.
Третья стадия - получение результатов на основе листа запросов, на ней производятся следующие действия:
) Перебирая лист запросов, вызываем требуемый ресурс, передав ему данные для запроса.
) Получаем страницу, передаем её соответствующему парсеру.
) Получаем от парсера лист с результатами по текущей задаче.
) Заносим этот лист в хеш таблицу, где ключ ФИО, следующий ключ это номер задачи с префиком (зависит от источника).
Четвертая стадия - анализ полученных данных. В нем определяется, решил ли пользователь задачу, количество попыток ее решения, уложился ли он в заданный временной интервал.
На выходе получаем:
) Хеш-таблицу с задачами, решенными в срок.
) Хеш-таблицу с задачами, решенными вне срока.
) Хеш-таблицу с количеством попыток.
) Хеш-таблицу с общим количеством полученных баллов (если рейтинг составляется на их основе).
) Лист инспектируемых студентов, отсортированный согласно занятому ими по результатам решения задач месту.
) Хеш-таблицу с местом конкретного участника (на случай если кол-во баллов или решенных задач одинаково, одного отсортированного листа будет недостаточно).
И завершающая стадия - это составление отчета, на основе данных, полученных на четвертой стадии.
Здесь алгоритм работы следующий:
) Получаем шаблон отчета.
) Подготавливаем шаблон - вставляем номера задач с префиксом в качестве заголовков столбца.
) Идя по списку участников:) Вставляем его результат по каждой задаче (кол-во попыток, итог решения), количество суммарных баллов, место.
) Сохраняем отчет в том месте, где указывает пользователь.
· Парсеры.
У парсеров есть общий класс родитель, он содержит в себе общие для всех парсеров методы и поля. Сам механизм извлечения нужных данных из html страниц у каждого парсера свой. На выходе они должны давать лист результатов.
Также для сайта кафедры есть еще один парсер, извлекающий баллы по каждой задаче.
· Классы составления GET запросов к сайтам.
У этих классов есть общий родитель, с методом, возвращающим строку GET запроса на основе данных для этого запроса
Каждый конкретный класс работает с определенным сайтом. Внутри себя хранит шаблон для составления GET запроса. Он устанавливает данные для запроса в нужные параметры и возвращает уже готовый GET запрос.
· Класс сбора результатов по сайтам.
Класс получает список данных для запросов, передает им соответствующим классам составления Get запроса и устанавливает соединение по полученной от них ссылке.
Полученную от сайтов страницу передает нужному парсеру, получает от него список результатов. Из них формирует таблицу результатов.
· Класс анализа полученных результатов.
Это важный класс - он будет заполнять структуры, описанные в четвертой стадии работы программы. Класс составления отчета может с легкостью обращаться к этим структурам, на их основе он и строит свой отчет.
· Класс составления отчета.
Работает с шаблоном отчета. В его функционал входят задачи:
) Подготовка шаблона (выставление заголовков задач с префиксом).
) Выставление для каждого испытуемого его результатов.
) Выдача отчета в виде строки.
Опираясь на решаемую ПП задачу, обозначим тот набор данных, который потребуется ей от пользователя, чтобы её решить. Прежде всего, это:
· ФИО инспектируемых студентов для вставки в отчет.
· Их ИД на требуемых ресурсах.
· Номера задач с указанием источника.
· Разбалловка задач (за исключением сайта кафедры).
· Период, в течении которого задачи должны были быть решены.
· Возможно, указать язык программирования, если курс специализирован.
· Вид составления рейтинга.
Чтобы занести эти данные, в интерфейсе должны содержаться элементы,
приведенные в табл. 1:
|
Смысловое значение |
Вид интерфейса |
|
ФИО, ИД для разных ресурсов |
Таблица, поля для добавления новых строк в таблицу |
|
Номера задач, разбалловка, источник |
Таблица, поля для добавления новых строк в таблицу |
|
Период |
Календарь |
|
Язык |
Текстовое поле |
Табл.1
Также должны быть функциональные элементы - кнопки, которые позволяют совершать пользователю следующие действия:
· Добавить строку в таблицу (на каждую таблицу).
· Удалить строку из таблицы (на каждую таблицу).
· Получить отчет.
Удобство привнесет наличие меню, которое будет содержать пункты:
· Сохранить/загрузить курс.
· Сохранить/загрузить список инспектируемых студентов.
· Инструкция.
|
Вкладка 1 |
Вкладка 2 |
|
Таблица пользователей. Кнопки добавить, удалить. Текстовые поля номера задач, баллы. Выпадающий список: источник. |
Таблица задач. Кнопки добавить, удалить. Текстовые поля ФИО, ID timus, ID acmp, ID atpp. |
|
Общее |
|
|
Меню (сохранить/загрузить курс, сохранить/загрузить список, инструкция) Календари: начало курса, конец курса Текстовое поле: язык программирования Выпадающий список: вид рейтинга |
|
Табл. 2
5. Реализация ПП
Согласно проекту ПП, реализуем необходимые классы и структуры.
Приведем набор реализованных классов в виде таблицы, см. табл.3
|
Название класса (набора классов) |
Описание |
|
|
Родитель/интерфейс |
Потомок |
|
|
HttpGetQuery |
HttpGetAcmp |
Строит запрос для acmp |
|
|
HttpGetAtpp |
Строит запрос для atpp |
|
|
HttpGetTimus |
Строит запрос для timus |
|
HtmlParser |
AcmpParser |
Парсер сайта acmp |
|
|
AtppParser |
Парсер сайта atpp |
|
|
AtppPointsParser |
Парсер сайта atpp (достает баллы у задач) |
|
|
TimusParser |
Парсер сайта timus |
|
- |
ResultMapper |
Отвечает за получение результатов |
|
- |
ResultItem |
Сущность одного результата, соответствует строке из таблицы результатов проверки с сайта |
|
- |
TaskList |
Составляет лист из всех задач с указанием источника |
|
- |
Tasks |
Сущность: содержит источник, массив номеров задач и баллов к ним |
|
- |
User |
Сущность, содержит ФИО и ИД для сайтов |
|
- |
UserList |
Составляет общий лист испытуемых |
|
- |
ControllerMain |
Отвечает за главный интерфейс |
|
- |
ControllerErrorMS |
Отвечает за интерфейс отображения ошибок |
|
- |
DataChecker |
Проверяет данные введенные пользователем на корректность |
|
- |
InternetConnector |
Устанавливает соединение с сайтом, возвращает страницу в виде String |
|
- |
Main |
Главный класс, отвечает за запуск приложения |
|
- |
Points |
Сущность для быстрого доступа к баллам, которые можно получить за решение конкретной задачи |
|
- |
QueryData |
Сущность, содержит все необходимое для построения GET запроса |
|
- |
Строит лист сущностей QueryData, на основе UserList и TasksList |
|
|
- |
ReportBuilder |
Занимается построением отчета |
|
- |
Template |
Содержит в себе шаблон отчета |
|
- |
TemplateSaver |
Класс-сущность для сохранения списков и курсов |
|
- |
Validator |
Хранит в себе фильтры, методы проверки |
Табл.3
Приведем код некоторых классов и методов:
public class AcmpParser extends HtmlParser {static final int INDEX_DATE = 1;static final int INDEX_TASK_NUMBER = 3;static final int INDEX_LANG = 4;static final int INDEX_STATUS = 5;static HttpGetAcmp httpGetAcmp = new HttpGetAcmp();
/**
* Конструктор, формирует лист с результатами {@link ResultItem}
*
* @param queryData
*/AcmpParser(QueryData queryData) {(queryData, httpGetAcmp);();();
}void createResultItemList() {(table.size() != 0) {(Element elem : table) {resultItem = new ResultItem()
.setTaskNum(getTaskNum(elem, INDEX_TASK_NUMBER))
.setStatus(getStatus(elem, INDEX_STATUS))
.setLang(getLanguage(elem, INDEX_LANG))
.setDate(getDate(elem))
.setSiteName(ACMP);(resultItem);
}
} else {resultItem = new ResultItem();.setStatus("none");(resultItem);
}
}Date getDate(Element elem) {{DateFormat.getInstance().parse(getTbByIndex(elem, INDEX_DATE).text());
} catch (ParseException e) {.printStackTrace();
}new Date();
}void addResultAll() {elements1 = document.getElementsByClass("gray");elements2 = document.getElementsByClass("white");page = 1;(!elements1.isEmpty() || !elements2.isEmpty()) {urlNextPage = urlSite + "&page=" + page;<Element> tempList = new ArrayList<>();.addAll(elements1.subList(0, elements1.size()));.addAll(elements2.subList(0, elements2.size()));(Element elem : tempList) {(isRequiredLang(elem, INDEX_LANG) || ControllerMain.language.isEmpty()) {.add(elem);
}
}= Jsoup.parse(ic.getHTML(urlNextPage));= document.getElementsByClass("gray");= document.getElementsByClass("white");
page++;
}
}
}
· Обработка нажатия кнопки "Получить отчет"
public void getReport(ActionEvent actionEvent) {= field_lang.getText();();= DataChecker.validData();(errors.isEmpty()) {userList = new UserList(new ArrayList<>(userObservableList));taskList = new TaskList(new ArrayList<>(taskObservableList));queryDataListBuilder = new QueryDataListBuilder(userList, taskList);resultMapper = new ResultMapper(queryDataListBuilder);reportBuilder = new ReportBuilder(resultMapper, taskList);resultFile = reportBuilder.reportBuild();(Main.getFileResult() == null) {.showAndSave(resultFile);
} else {.saveHtmlDocument(resultFile);
}
} else {.createErrorsMS();
}
}
· Класс построения отчетаclass ReportBuilder {static final String TEMPLATE = "row";static final String POINTS = "points";static final String LEVEL = "level";static final String NAME = "name";static final String SOLVE_CLASS = "solve";static final String OLD_SOLVE_CLASS = "oldsolve";static final String NOT_SOLVE_CLASS = "notsolve";static final String PLUS = "+";static final String MINUS = "-";static final String POINTS_HEAD = "pointsHead";Element rowTemplate;Element headerTemplate;Element tdTemplate;Document table;String lastIdRow = "header";String lastIdColumn = "fio";HashMap<String, HashMap<String, List<ResultItem"> resultMap = new HashMap<>();StoreBuilder sb;ResultMapper resultMapper;(ResultMapper resultMapper, TaskList taskList) {.resultMapper = resultMapper;= resultMapper.getResultMap();= new StoreBuilder(resultMapper);html = Template.template;= Jsoup.parse(html);(sort.equals(SORT_BY_SOLVE)) {.getElementById(POINTS_HEAD).remove();
}();();(taskList);(taskList);();
}void createReport() {<String> names = sb.getStoreList();status, style;(String name : names) {<String> taskNums = resultMap.get(name).keySet();row = createNewResultRow(name);.getElementById(NAME).html(name);.getElementById(LEVEL).html(getPlace(name));(sort.equals(SORT_BY_POINTS)) {.getElementById(POINTS).html(getPoints(name));
}(String task : taskNums) {<ResultItem> resultItems = resultMap.get(name).get(task);= isSolveOrOldSolve(name, task) ? PLUS : MINUS;(status.equals(PLUS)) {= (sb.isSolve(name, task)) ? SOLVE_CLASS : OLD_SOLVE_CLASS;
} else {= NOT_SOLVE_CLASS;
}.getElementById(task).addClass(style);.getElementById(task).html(resultMapper.getFio_task_tryCount_map().get(name).get(task) + status);
}.getElementById(lastIdRow).after(row.toString());= name;
}
}String getPoints(String name) {(sb.getFio_solvePoints_map().get(name) != null) ? String.valueOf(sb.getFio_solvePoints_map().get(name)) :
"0";
}String getPlace(String name) {String.valueOf(sb.getFio_place().get(name));
}boolean isSolveOrOldSolve(String name, String task) {sb.isSolve(name, task) || sb.isOldSolve(name, task);
}void setTaskColumns(TaskList taskList) {[] taskNum = taskList.getAllTasks();newTh;(String aTaskNum : taskNum) {= createNewTh(aTaskNum);.getElementById(lastIdColumn).after(newTh.toString());= aTaskNum;
}
}reportBuild() {table.toString();
}Element createNewTemplateTd(String id) {newTd = tdTemplate.clone();.attr("id", id);newTd;
}void setRowTemplate(TaskList taskList) {= table.getElementById(TEMPLATE);.getElementById(TEMPLATE).remove();(sort.equals(SORT_BY_SOLVE)) {.getElementById(POINTS).remove();