Студенты в Школе разработки интерфейсов делятся на команды. В процессе обучения студенты выполняют задания. Оценки за них ставятся либо конкретному студенту, либо всей команде студентов. В конце Школы перед менторами стоит задача распределения студентов. Каждый ментор составляет приоритезированный список студентов, в которых он заинтересован. Каждый из студентов также составляет приоритезированный список менторов, к которым он бы хотел пойти.
Создайте библиотеку, которая реализует такой программный интерфейс:
- добавление студентов-участников и объединение их в команды;
- создание командных и индивидуальных заданий;
- выставление оценок за задание;
- создание приоритезированных списков менторов и студентов;
- решение задачи распределения студентов среди менторов в соответствии с приоритезированными списками.
Выполнение одной или нескольких дополнительных задач будет плюсом:
- организуйте процесс сериализации/десериализации в разных форматах данных;
- создайте тесты к библиотеке;
- реализуйте веб-интерфейс или интерфейс командной строки для своей библиотеки.
Определяю, какие данные необходимо будет хранить. Выделяю четыре сущности:
- Задания;
- Студенты;
- Команды студентов;
- Менторы.
Решаю начать с наиболее независимой сущности. Описываю задание:
{
id: 1,
type: 'individual' || 'command',
name: '',
description: '',
}
Каждое задание имеет уникальный идентификатор id
. Самое простое решение
для генерации этого идентификатора, не требующее особых ресурсов — хранить id
последнего созданного задания и пре-инкрементировать его при добавлении нового
задания:
{
id: ++lastTaskId,
}
Пишу методы для добавления и получения заданий.
Сразу думаю о том, что у студентов и команд будут храниться идентификаторы заданий вместе со статусом выполнения и оценкой, так что при удалении задания следует позаботиться также и об удалении ссылки на него в студентах и командах.
Описываю команду студентов:
{
name: '', // уникальное поле
members: [], // содержит идентификаторы участников
tasks: [], // содержит данные о заданиях и прогрессе выполнения
}
Как должны выглядеть данные о заданиях и прогрессе выполения у команд и студентов? Минимальный набор необходимых данных:
{
tasks: [
{
id: 1,
completed: false,
score: null,
},
{
id: 1,
completed: true,
score: 10, // десятибалльная шкала
}
],
}
На основе представленных выше данных реализую интерфейс для добавленя и удаления команд, студентов и менторов.
Кроме того, реализую методы для назначения задач и выставления оценок за задачи. Важно учесть то, что командные задачи не могут быть назначены отдельным студентам и наоборот. Кроме того, если мы удаляем задачу, важно удалить её не только из общего списка задач, но и убрать её из назначенных у выполняющих её участников.
Студента описал следующим образом:
{
id: 1,
fullname: 'John Doe',
tasks: [],
mentor: null,
team: null,
preferredMentors: [],
}
Поле team
позволяет проверить, принадлежит ли студент к какой-либо команде.
При удалении команды это поле автоматически сбросится на null
у всех бывших
участников удалённой команды.
Самая сложная часть задания — создание приоритезированных списков студентов и менторов, а также их распределение в соответствии с приоритетами.
Для начала нужно просто решить, в каком виде будут храниться приоритеты. Они
должны быть довольно простые, то есть грубо говоря от наиболее предпочтительного
к наименее предпочтительному, без дополнительных шкал. Поэтому можно просто
хранить список идентификаторов в массиве, где элемент с индексом 0 — наиболее
предпочтительный, а элемент с индексом length - 1
— наименее предпочтительный:
// Приоритезированный список менторов у студента
{
mentors: [1, 3, 7, 4, ...],
}
// Приоритезированный список студентов у ментора
{
students: [9, 26, 2, 5, ...],
}
Задача с распределением студентов и менторов по приоритетам для меня довольно нетривиальна. Во время размышлений над способом распределения решил каждому приоритету поставить в соответствие определённый «вес» — чем выше приоритет, тем больше вес. Затем сравнивать каждую пару «студент — ментор» и в соответствии с поставленными ими приоритетами считать её вес. Например, если они оба будут стоять друг у друга в наивысшем приоритете, вес у из пары получится тоже наивысший. После подсчёта веса каждой пары останется только распределить пары по весу, от наибольшего к наименьшему. Кроме того, при распределении нужно будет ограничить максимальное количество студентов у одного ментора, чтобы добиться равномерности.
В самом простом случае у каждого студента должен быть составлен приоритезированный список всех наставников, а у каждого наставника — приоритезированный список всех студентов. Однако мы живём не в идеальном мире, и, скорее всего, каждый наставник/студент занесут в список лишь несколько человек, в которых они заинтересованы больше всего. Будем считать, что те, кто не был занесён в список, имеют наименьший приоритет (и, соответственно, вес).
Таблица с весом каждой пары «студент/ментор» будет иметь такой вид:
[
{
studentId: 1,
mentorId: 1,
value: 20,
},
{
studentId: 3,
mentorId: 5,
value: 4,
}
]
После распределения у каждого студента в поле mentor
будет лежать
идентификатор наставника, а у каждого наставника в поле students
будет лежать
список идентификаторов его студентов.