GithubHelp home page GithubHelp logo

petr-kalinin / algoprog Goto Github PK

View Code? Open in Web Editor NEW
39.0 7.0 18.0 23.08 MB

License: GNU Affero General Public License v3.0

JavaScript 0.24% Shell 0.01% CoffeeScript 96.69% CSS 0.59% Batchfile 0.01% Python 0.71% Dockerfile 0.06% TypeScript 1.72%

algoprog's People

Contributors

blighting avatar daniiars avatar danilazhebryakov avatar danilvolkov avatar danya777830 avatar dependabot[bot] avatar katy1819 avatar linburk avatar mathbattler avatar petr-kalinin avatar ulinegor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

algoprog's Issues

Добавить редактирование профиля

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

  1. Доработать страничку /user/, чтобы пользователь мог бы менять часть своих данных (аналогично тому, как сейчас админ может менять данные)
  2. Доработать ручку /api/setUser, чтобы она разрешала пользователю менять свои данные.

Разобраться, почему setOutcome работает так медленно

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

Поддерживать переименования таблиц

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

Надо полностью очищать базу задач и таблиц в начале вызова downloadContests.

Идеально бы этот тикет сделать вместе с #29 .

Добавить error boundary

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

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

Наладить тестирование

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

Доделки ачивок

  1. Ачивка А не должна отбираться, если активность упала.
  2. Надо выставлять правильный key, чтобы не было дубликатов, даже если есть две одинаковые ачивки
  3. Ачивка full не учитывает, что есть шоколадка за один контест
  4. Поменять расстояние от имени до ачивок

Отдавать результаты школьника по отдельному контесту

Сейчас раскраска задач на страничке контеста (решена - не решена) делается на основе ручки /api/userResults/, которая отдает результаты сразу по всем задачам и таблицам. Для раскраски задач эти все данные не нужны. Хорошо бы сделать ручку, которая будет отдавать только результаты задач одного контеста.

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

Рефакторинг кода

Код алгопрога довольно грязный. Хорошо бы его подчистить.

Возможные направления следующие (их не обязательно делать в этом порядке, можно в произвольном):

  • Разобраться с зависимостями по import'ам. Сейчас серверный код (server) может зависеть (по import'ам) от клиентского кода (client) и наоборот. Надо как минимум добиться того, чтобы клиентский код не зависел от серверного, а в идеале сделать и так, чтобы серверный код не зависел от клиентского (кроме как в serverSideRendering); для последнего надо будет выделить отдельный каталог lib на том же уровне, что и server и client, и вынести туда код, который нужен и серверу, и клиенту.

  • Избавиться от понятия страниц (client/pages) в клиентском коде. Изначально логика была в том, чтобы "страницы" загружали данные с сервера (т.е. были ConnectedComponent'ами), а компоненты не загружали бы код. Но со временем все перепуталось, и сейчас кажется, что pages — это не особо нужная прослойка. Их как минимум надо перенести в components, а от большинства стоит избавиться.

  • Разобраться с файлами в client/components. Там свалка, причем еще зачастую очень непоследовательно разбитая по файлам — в некоторых файлах сразу несколько компонентов, в некоторых наоборот один мелкий. Это исторический эффект: более старые части сайта разбиты по многим файлам (особенно сводные таблицы), более новые я стараюсь писать в одном файле. Надо как-то это упорядочить, и скорее всего ввести иерархию каталогов внутри components, а не все пихать в один каталог components.

  • Разобраться и переписать setupApi. Надо как минимум нормально использовать возможности express — там вроде есть способ "правильно" сделать такой "роутинг" в пределах одного префикса (/api), а в идеале максимально упростить обработку каждой ссылки, вынеся весь существенный код в отдельные файлы.

  • Переименовать каталог cron во что-нибудь другое, например, в downloads, и вынести оттуда updateResults

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

Объединить downloadContests и downloadMaterials

Сейчас есть две отдельных функции -- downloadContests и downloadMaterials. Они делают схожие вещи — скачивают данные с информатикса — но одна формирует коллекцию Materials, а другая — Problems и Tables.

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

Надо их объединить в одну. Нечто похожее (только в сильно более простом варианте) уже реализовано на ветке shad. Надо оставить общий код обхода всего курса на информатиксе (тот код, который сейчас в downloadContests), но сделать так, чтобы сам код ничего никуда не сохранял, а вызывал бы методы двух классов, один из которых отвечал бы за создание materials, а другой — за создание contests и tables. А сначала надо аккуратно продумать архитектуру этого всего.

Добавить возможность комментировать конктерные строчки кода

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

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

Формат я ожидаю примерно такой:

здесь идут общие комментарии  

:5:for i in range(0, n):
можно писать просто 
range(0)

:15:print(x)
лучше делать как-то еще

::
еще общие комментарии

т.е. в текст комментария добавляются строки специального вида, содержащие (через двоеточие) номер строки исходного кода и текст этой строки (текст — для человекочитаемости), и считается, что весь текст комментария дальше до следующей специальной строки относится именно к этой строке. Весь текст, идущий до первой специальной строки, считается общим комментарием. Есть также специальная строка "::", обозначающая, что дальнейший текст тоже считается общим комментарием.

Соответственно, в примере выше текст "можно писать просто range(0)" относится к пятой строке исходного кода, текст "лучше делать как-то еще" к 15-й, а строки про общие комментарии являются общими комментариями.

В этом тикете надо:

  1. Допродумывать такой формат
  2. Сделать так, чтобы в интерфейсе комментирования решения можно было кликать по строчке кода и в форму для комментария под решением добавлялась бы соответствующая специальная строка
  3. В интерфейсе просмотра решения добавить разбиение комментария по специальным строкам и отображение в решении. Возможно, изучить, нет ли у react'а готового компонента для этого.
  4. В интерфейсе комментирования решения добавить оботражение комментариев внутри решения и возможность их редактирования
  5. Возможно, добавить возможность просмотра комментариев к старым сабмитам в интерфейсе комментирования новых сабмитов

Добавить кнопки для админских действий

Сейчас ряд админских действий можно выполнить только путем ввода url /api/... напрямую. Например, это updateResults (сделано), downloadSubmits, downloadMaterials и т.д.

Хорошо бы для всех таких действий добавить куда-нибудь соответствующие кнопки или ссылки. Кнопки/ссылки надо поместить на те странички, куда они логичнее всего подходят (updateResults — на страничку пользователя, и т.п.); заодно хорошо бы добавить ручку downloadSubmitsForUserAndProblem и добавить ее на страничку review, заодно сделав, чтобы страничка review работала и в случае, когда посылок по задаче не было вообще.

Общие ссылки (downloadMaterials и т.п.) можно добавить на страничку dashboard.

Разобраться с кешированием

Почему-то сейчас браузер при каждом обновлении вкладки скачивает заново все, что нужно, в том числе bundle.js, который довольно большой. Надо сделать так, чтобы браузер кешировал не изменившиеся ресурсы и не скачивал их заново. Скорее всего, для этого надо понять, как к express подключить etag.

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

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

Доработать систему регистрации на занятие

Надо доработать по мелочам систему регистрации на занятия в ННГУ. А именно:

  1. Сделать кнопку "удалить все регистрации", доступную только для админов. Для этого надо добавить соответствующую ручку в api и добавить кнопку на страницу регистраций.
  2. Добавить поддержку даты, на которую все регистрируются, указание ее на страничке регистраций, возможность админам менять ее, и отключение возможности записи на занятие, если эта дата уже прошла. Тут самый нетривиальный вопрос -- где на сервере хранить эту дату, в остальном вроде все довольно понятно -- пара ручек в api и добавить нужные поля на страничку. На сервере уже есть табличка Config, там это можно хранить.

"Скачать" хорошие решения

Сейчас в списке хороших решений есть ссылка "скачать", но у обычного пользователя она обычно не работает.

Дело в том, что она направляет на стандартную ручку скачивания решения (такую же, как скачивание решения при просмотре решения по ссылке "подробнее" в списке посылок). А эта ручка ничего не знает про то, что решение "хорошее", и поэтому проверяет права доступа и не позволяет скачать решение никому, кроме автора решения или админа.

Надо это как-то пофиксить.

Лучше всего в ручке скачивания решения проверять: если решение хорошее, то проверить, решил ли скачивающий пользователь эту задачу и, если решил, то разрешить скачивание (аналогично тому, какие проверки написаны в ручке "хороших решения").

Если это сложно, то просто убрать ссылку "скачать" из хороших решений.

Добавить возможность отвечать на комментарии

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

А именно, как только пользователи начинают отвечать на комментарии, это превращается в диалог. А диалог намного удобнее вести в более приспособленных для этого системах — в диалогах во вКонтакте, в телеграмме, по почте и т.д. Это намного удобнее потому, что там есть оповещения о новых и непрочитанных сообщениях, что можно работать с телефона, и т.д.

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

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

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

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

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

UPD: в ejudge есть что-то очень похожее: https://ejudge.ru/wiki/index.php/Бот_для_telegram

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

Доработки тестов в ejudge

После того, как поднимем свой ejudge (#6), надо доработать тесты по отдельными задачам. В этом тикете собираю пожелания по таким доработкам:

  1. Перевести задачу "Дремучий лес" с уровня 5А на ejudge, снизив требуемую там точность, а задачу с той точностью как там сейчас написано, отправить на 5В.
  2. В задаче "IP-адрес" добавить тест вида XXXXXXXXXXX..., где X -- это цифры, так, чтобы полученное число при переполнении попадало в интервал от 0 до 255.
  3. В задаче "Степень" добавить тест 81, ответ 6
  4. В задаче "уравнение" добавить тест "0 4 -2"
  5. Наделать тестов для задачи "автобусы"
  6. Наделать тестов к Франциску Ксавьеру
  7. Добавить тесты к Pink Floyd (все -1 на переполнение -INF, а также тесты из комментариев)
  8. В задаче "Круглые числа" https://algoprog.ru/material/p245 добавить тест 1
  9. В задаче "Объединение последовательностей" https://algoprog.ru/material/p1442 добавить небольшие тесты, где ответом является куб, а не квадрат
  10. Вообще, поискать по комментариям слово "контрпример" и подобавлять соответствующие тесты

Сохранять redux-данные в localstorage

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

Надо сделать так, чтобы данные из редукса сохранялись бы в localstorage и при любых изменениях данных редукса данные в localstorage обновлялись бы. При этом надо еще как-то поддержать то, что в localstorage будут писаться данные из разных вкладок, а также поддержать работу с PRELOADED_STATE.

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

Обновить пакеты

Обновить используемые npm-пакеты. Заодно может быть перейти на новую версию node.js.

Убрать использование registeredUser.findByKey

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

То же надо сделат для findByKeyWithPassword.

После этого можно findAllByKey переименовать обратно в findByKey.

Показывать теорию на странице контеста

Сейчас теория по очередной теме показывается только на странице уровня. На странице контеста (по ссылке типа "2В: Задачи на ...") теория не показывается. Это зачастую очень неудобно, потому что при переходе по ссылкам в левом меню для просмотра теории кажется естественно кликнуть на название темы, а не на уровень.

Надо показывать теорию над списком задач.

Для этого надо:

  1. Поменять downloadMaterials, чтобы внутрь материала "contest" добавлялась соответствующая теория;
  2. Поменять компонент, отображающий контест, чтобы он был готов к тому, что там будут не только задачи.

Сохранять предпочтения по похожим языкам

Есть школьники, которые пишут в основном на Free Pascal, а есть школьники, которые пишут в основном на Pascal ABC. Форма отправки задачи пытается угадать язык решения по расширению файла, но она не можешь отличить FP от PABC, потому что оба компилятора используют расширение .pas. Аналогичная ситуация (хотя и заметно менее острая) — с питоном.

Надо научиться угадывать язык с учетом статистики сабмитов конкретного пользователя. А именно, если пользователь чаще сдавал на PABC, а не на FP, то при попытке сдать решение .pas автоматически выбирать в форме PABC.

Для этого надо:

  1. В объект user добавить поле со статистикой сабмитов (типа "язык -> количество сабмитов")
  2. В updateResults заполнять это поле
  3. В lib/languages.coffee добавить актуальные расширения для всех языков (сейчас .pas указано только у одного языка — собственно, у FP)
  4. В submitForm смотреть на статистику пользователя и из списка языков с подходящим расширением выбирать наиболее популярный именно у этого пользователя.

Переделать табличку solvedByWeek

Сейчас в табличке solvedByWeek и в профиле пользователя отображаются результаты за последние недели, начиная с некоторой фиксированной даты. Это довольно неудобно. Хочется сделать так, чтобы отображались результаты за последние N недель, где N фиксировано (например, 16).

Для этого надо:

  1. Переделать расчет byWeek в updateResults
  2. Переделать отображение таблички по образцу календаря, чтобы она была компактнее (в частности, можно сделать общий заголовок для нескольких подряд идущих колонок)
    3. Убедиться, что каждую ночь все результаты рассчитываются заново

Настроить турбо-страницы

Нужно настроить турбо-страницы (https://yandex.ru/dev/turbo/doc/concepts/index-docpage/). RSS надо генерировать автоматически из всех доступных материалов (например, рекурсивным обходом дерева). На каждую страницу стоит добавить также меню, и посмотреть, какие еще возможности есть в турбо-страницах.

Добавить поддержку админов групп

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

Надо:

  1. В объекте User сейчас есть поле admin, которое имеет тип boolean. Надо сделать так, чтобы там мог быть список строк (userList).
  2. Добавить функцию isAdminFor(user1, user2), который будет проверять, что один пользователь может админить второго. Добавлять надо в client/lib, чтобы функция была доступна и на клиенте, и на сервере.
  3. Проверить все места, где проверяется user.admin (включая вызовы isAdmin) и где надо поправить на isAdminFor.

(Близкая задача сделана здесь: e387958 , эту задачу надо делать на основе этого коммита)

Проверить статус PS

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

UPD: видимо, надо сразу при создании решения вызывать updateResults, а не только после отправки.

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

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

Для этого надо:

  1. Добавить ручку api, возвращающую последние посылки, или еще лучше -- определенную "страницу" посылок (для пагинации).
  2. Добавить соответствующую страницу, добавить на нее ссылку со страницы профиля
  3. Сделать то же с результатами, а не с сабмитами, добавить на страничку переключатель.

Привести списки таблиц в порядок

Сейчас спикси возможных сводных таблиц захардкожены в downloadMaterials и в api/table.coffee , и возможно где-то еще, причем в разных видах. Надо их унифицировать (вынести в отдельный файл и везде использовать дальше одну и ту же переменную), а в идеале не хардкодить вообще, а ходить в базу Tables или т.п.

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

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

Если же внутренняя ссылка встречается внутри странички (например, в разделе "О курсе"), то происходит полная перезагрузка страницы.

Надо это исправить. Я не очень понимаю, что нужно сделать для этого, видимо, надо как-то средствами React router перехватывать клики по таким ссылкам. Вроде в React router я видел готовый компонент для этого.

Добавить поддержку разных материалов для разных userList

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

Тут надо тщательно продумать, как это делать, в частности, чтобы админ соответствующего userList мог бы редактировать свои материалы, и сделать.

Добавить календарь в профиль пользователя

В профиле пользователя сейчас отображается одна строка из таблички solvedByWeek. Хочется сделать ее более детализированной -- сделать что-то типа календаря, как отображается на страничке профиля в github (раздел "contributions in the last year"). Также хорошо бы, чтобы при наведении или при щелчке на клетку в календаре отображалась бы подробная информация о соответствующем дне (какие задачи пользователь решал в этот день, или просто список его сабмитов и т.д.)

Для этого надо:

  1. Найти подходящий компонент для React
  2. Если надо, то локализовать его (перевести на русский, убедиться, что воскресенье -- последний день недели)
  3. Добавить расчет нужных данных в updateResults и хранение их в объекте User или в каком-нибудь отдельном объекте. Рассчитывать и хранить надо только последние сколько-то недель.
  4. Добавить календарь на страничку пользователя.
  5. Добавить обработку наведения или клика мышкой по клетке, если компонент это поддерживает.

Научиться поддерживать разные тестирующие системы

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

Хочется сделать так, чтобы разные задачи могли тестироваться на разных тестирующих системах — помимо информатикса это в первую очередь ejudge (ejudge.algoprog.ru) и codeforces.

На ветке shad уже есть код работы с ejudge (та ветка умеет работать только с ejudge, но не с информатиксом), но там надо его доработать, потому что сейчас он выгружает вообще все сабмиты контеста каждый раз. Также уже основной код для поддержки разных теструющих систем есть в каталоге testSystems.

Для этого надо:

  1. В классе Problem добавить поле testSystem, указывающее, на какой тестирующей системе эта задача. Скорее всего в класс Material тоже надо добавить это поле.
  2. При сабмите задачи и при выгрузке результатов надо смотреть на поле testSystem задачи и ходить в нужную тестирующую систему
  3. Доработать код работы с ejudge, чтобы он не выгружал все вообще сабмиты
  4. Поправить все случаи упоминания информатикса в коде (в частности, ссылки "задача на информатикс" в условиях задач) — там тоже надо ходить в объект Problem и писать разный текст.
  5. Продумать, как работу с несколькими тестирующими системами поддержать в downloadMaterials (скорее всего по аналогии с тем, как редактируются задачи на алгопроге, т.е. через поле force) это сделается в #95
  6. Перевести пробную задачу на ejudge (например, Дележ яблок - 1), убедиться, что все работает
  7. Перевести все уровни 1А-1Б на ejudge;
  8. Добавить поддержку codeforces

UPD: Для ejudge вообще лучше сделать работу через webhook, т.е. чтобы не алгопрог ходил бы регулярно на ejudge за новыми посылками, а чтобы ejudge когда надо дергал бы определенное api алгопрога.

Разобраться с кодировками сабмитов

Ппоследние visual studio по умолчанию сохраняют решения в utf16 (наблюдал как минимум у некоторых пользователей). Надо перекодировать такие решения в utf8 при сабмите, иначе gcc дает ошибку компиляции.

Кроме того, информатикс конвертирует все решения в utf8 при сабмите, причем иногда делает это неправильно, в результате чего на алгопроге может получиться решение в неправильной или вообще некорректной кодировке (для примера можно отправить решение с русским словом "тест"). Надо подменять полученное с информатикса решение обратно на то, которое отправлял пользователь. При этом надо не сломать дедупликатор сабмитов.

Обновлять результат после засчитывания

На страничке просмотра результата (reviewResult), если зачесть решение (или проигнорировать, прокомментировать или дисквалифицировать), запрос на сервер на засчитывание делается, но сама страничка не обновляется. Надо после успешного запроса на засчитывание обновить сам результат. Для этого надо в ReviewResultPage прописать свойство handleDone для ReviewResult, в обработчике вызвать @props.reload.

Хранить строчки сводных таблиц

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

Поэтому чтобы нарисовать таблицу, приходится запросить объекты "результат" для каждой ячейки, что довольно долго. Можно предподсчитывать и хранить сразу целые строки таблицы. А именно, в каждом "результате", который отвечает за таблицу, хранить также массив вложенных результатов; или делать это не в самом объекте "результат", а в отдельной таблице БД.

Надо:

  1. Проверить, что тормоза таблицы связаны именно с большим количеством объектов. Возможно, получится ускорить процесс просто за счет того, чтобы за один запрос к БД получать сразу много нужных результатов.
  2. Если не получилось, то в updateResults надо в результаты, соответствующие контестам и таблицам (где total > 1) записывать информацию о "дочерних" результатах. Или завести отдельную таблицу в БД и записывать эту информацию туда.
  3. Переделать /api/table, чтобы использовались новые объекты.

Обновлять результат при повторном открытии

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

Особенно это заметно на страничке reviewResult. Если админ зашел на эту страничку, посмотрел сабмиты пользователя, потом пользователь отправил новое решение, и админ опять зашел на эту страничку, админ увидит закешированные данные.

Надо сделать так, чтобы данные обновлялись при заходе на страничку. Для этого, скорее всего, потребуется внести изменения в ConnectedComponent, чтобы он при наличи соответствующего параметра выполнял invalidateData сразу при рендеринге.

Улучшить server side rendering

Для отрисовки многих компонентов на клиенте надо сделать запрос на сервер за данными. Для отрисовки тех же компонентов на сервере в рамках server side rendering тоже делаются такие же запросы внутри сервера.

Тут есть две проблемы.

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

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

Надо придумать, как сделать, и решить обе проблемы.

Добавить понятие "спящего" пользователя

Пользователи, которые давно неактивны (например, занимались несколько лет назад) на самом деле довольно сильно мешаются. Они загромождают вывод странички registeredUsers, затормаживают работу updateAllResults, делают по сути невозможным запуск downloadAllSubmits и т.д. — вообще, любые операции, работающие со всеми вообще пользователями, тормозят. Плюс есть пользователи, которые зарегистрировались, но не пишут мне, чтобы я их активировал.

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

  1. В класс User добавить поле-флаг dormant и поле-дату lastActivated
  2. Поле lastActivated устанавливать в Date.Now() при регистрации пользователя, а также при каждой смене userList'а (т.е. активации/деактивации пользователя).
  3. В updateResults устанавливать этот флаг если, например, последний сабмит пользователя был более 3 месяцев назад И userList==unknown И поле lastActivated более чем 3 месяца назад;
  4. На страничке профиля пользователя добавить возможность админу вручную устанавливать флаг dormant (там сейчас рядом с разными userList есть кнопка «—»); также при перемещении пользователя в другой userList сбрасывать ему флаг dormant;
  5. Во всех местах, где идет запрос всех пользователей, возвращать только тех, у кого dormant=false (в первую очередь поправить find* функции в user.coffee);
  6. Блокировать вход в систему для пользователей с флагом dormant (по аналогии с тем, как делается для неоплаченных пользователей);
  7. На страничку registeredUsers (где за счет п. 4 теперь останутся только не-спящие пользователи) добавить возможность поиска по имени и по логину, чтобы там можно было найти спящих пользователей (и пользуясь п. 3 сделать их обратно не-спящими)

Сделать компактную табличку списка сабмитов

Сейчас табличка списка сабмитов довольно широкая.
Надо сделать так, чтобы на узких мониторах или в узких условиях она была компактнее. Например, заменить ссылку "подробнее" на какой-нибудь значок из FontAwesome (например, на глаз). Можно еще сделать строчки многострочными (типа время и результат -- на одной строке, время, память и т.д. -- на строчку ниже).

Это важно в первую очередь для ReviewResult, где табличка рисуется справа.

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

Перейти на новую систему сборки

Сейчас для сборки всех исходников в один js-бандл используется система gulp. Насколько я понимаю, она далеко не современная, сейчас вроде модно использовать webpack. Хочется перейти на него.

  1. Выяснить, действительно ли webpack сейчас является state of the art в node.js бандлинге
  2. Разобраться, как webpack научить работать с coffeescript (в прошлый раз у меня именно на этом этапе возникли проблемы)
  3. Перевести сборку алгопрога на webpack

Дальше можно это развивать, подключая разные понтовые фичи webpack'а типа hot reload и т.д. Еще хорошо бы разобраться с production/testing окружениями и сделать так, чтобы в продакшене использовались какие-нибудь прод-фичи (например, минификация).

Разрешить админу менять пароль школьника

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

Но можно для простоты сделать так, чтобы админы могли сбрасывать пароли пользователей. Это довольно легко. Надо добавить в UserBadge рядом с другими полями, которые может менять админ, поле для нового пароля, а на сервере менять пароль через методы registereduser (который использует passportLocalMongoose).

Обновлять данные через вебсокеты

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

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

Автоматическое обновление происходит за счет регулярных (обычно раз в 20 секунд) перезапросов данных с сервера. Это довольно плохо по многим причинам (дает нагрузку на сеть и на сервер, приносит новые данные не мгновенно и т.д.)

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

Это релизуется технически за счет, во-первых, веб-сокетов, во-вторых послеоперационных хуков в mongoose.js (post-hooks, они же middleware). Веб-сокеты позволяют установить долгоживущее соединение между клиентом и сервером, чтобы сервер мог передать данные клиенту когда будет надо. Хуки позволяют выполнять нужные нам функции после того, как данные в монге обновились.

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

При этом тут есть два варианта реализации:

  • делать отдельные хуки для каждого типа обновляемой информации. Например, для обновления сабмитов по задаче. Это просто, потому что понятно, какой именно хук надо делать и какие именно данные надо отправлять. Но для каждого типа передаваемой информации нужен будет отдельный код.
  • придумать универсальный подход, чтобы на любую вообще ручку api можно было подписываться, т.е. создавать веб-сокет, в который будет посылать обновленную информацию по этой ручке. Это сложно, т.к. ручка внутри себя может делать очень много запросов к монге, и надо их как-то выделять и запоминать, что при их изменении надо перевычислить ручку и отправить данные клиенту. (Точнее, тут речь скорее только про get-ручки, которые не изменяют данные в монге. Подписываться на post-ручки типа submit, конечно, не имеет смысла). Для этого надо сделать прослойку между mongoose и кодом сервера, чтобы при любой операции доступа к монге она запоминала бы, что была такая операция, и создавала бы нужный хук. Возможно, есть уже готовые такие фреймворки, надо сначала поискать.

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

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.