Corp/docs/completed/POLLS_IMPLEMENTATION.md
Игорь Надеин ee6e840252 Реорганизация документации и скриптов проекта
- Создана структура docs/ с категориями: completed, guides, architecture, diagnostic, in-progress, reports
- Перемещено 47+ MD файлов из корня проекта в docs/
- Удалено 6 устаревших/дублирующихся файлов
- Создана структура backend/scripts/ для тестов и утилит
- Перемещено 35 Python скриптов в backend/scripts/
- Создана структура backend/docs/ для технической документации
- Перемещено 12 MD файлов backend в backend/docs/
- Перемещены LDAP тестовые данные в backend/tests/fixtures/
- Добавлены README файлы для навигации
- Добавлены правила в .github/copilot-instructions.md для размещения документации

Результат:
- Корень проекта: только README.md
- Корень backend/: только manage.py и Django приложения
- Вся документация организована в структурированные папки
2025-12-26 13:49:51 +03:00

362 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Реализация голосований (Polls) в мессенджере
## Обзор
Добавлена полная система голосований в мессенджер EUSRR, реализованная по примеру современных мессенджеров (Telegram, WhatsApp).
## Возможности
### Типы голосований:
1. **Обычное голосование** - выбор одного варианта
2. **Множественный выбор** - выбор нескольких вариантов
3. **Анонимное голосование** - не показывается кто голосовал
4. **Викторина** - с правильным ответом, который показывается после голосования
### Функции:
- Создание голосования с 2-10 вариантами ответа
- Визуализация результатов в реальном времени (прогресс-бары)
- Автоматическое закрытие голосования по времени
- Отображение количества проголосовавших
- Изменение голоса (можно переголосовать)
- Real-time обновление через WebSocket
## Структура базы данных
### Модели (backend/communications/models.py)
#### Poll
```python
- message: OneToOne -> Message (связь с сообщением)
- author: FK -> User (создатель)
- question: CharField (вопрос)
- is_anonymous: Boolean (анонимность)
- is_multiple_choice: Boolean (множественный выбор)
- is_quiz: Boolean (викторина)
- allows_custom_answers: Boolean (свои варианты)
- is_closed: Boolean (закрыто)
- closed_at: DateTime (время закрытия)
- closes_at: DateTime (автозакрытие)
- total_voters: Integer (количество проголосовавших)
```
#### PollOption
```python
- poll: FK -> Poll
- text: CharField (текст варианта)
- position: Integer (порядок)
- vote_count: Integer (количество голосов)
- is_correct: Boolean (правильный для викторин)
```
#### PollVote
```python
- poll: FK -> Poll
- option: FK -> PollOption
- voter: FK -> User
- voted_at: DateTime
UniqueConstraint: poll + voter + option
```
## API Endpoints
### 1. Создать голосование
```http
POST /api/v1/communications/polls/create/
Body:
{
"chat_id": 1,
"question": "Какой язык программирования лучше?",
"options": ["Python", "JavaScript", "Go", "Rust"],
"is_anonymous": false,
"is_multiple_choice": false,
"is_quiz": false,
"correct_option_index": null, // для викторин
"closes_in_minutes": 60 // опционально
}
Response:
{
"success": true,
"poll_id": 123,
"message_id": 456
}
```
### 2. Проголосовать
```http
POST /api/v1/communications/polls/{poll_id}/vote/
Body:
{
"poll_id": 123,
"option_ids": [1] // или [1, 2] для множественного
}
Response:
{
"success": true,
"results": {
"question": "...",
"total_voters": 10,
"is_closed": false,
"options": [...]
}
}
```
### 3. Закрыть голосование
```http
POST /api/v1/communications/polls/{poll_id}/close/
Body:
{
"poll_id": 123
}
Response:
{
"success": true
}
```
### 4. Получить результаты
```http
GET /api/v1/communications/polls/{poll_id}/results/
Response:
{
"question": "...",
"total_voters": 10,
"is_closed": false,
"is_anonymous": false,
"is_multiple_choice": false,
"is_quiz": false,
"user_voted_option_ids": [1],
"options": [
{
"id": 1,
"text": "Python",
"vote_count": 5,
"percentage": 50.0,
"is_correct": false,
"voters": [...] // если не анонимное
}
]
}
```
## Frontend компоненты
### JavaScript модуль (chatPoll.js)
```javascript
import ChatPoll from './components/chatPoll.js';
// Инициализация
const chatPoll = new ChatPoll({
chatId: 1
});
// Создание голосования через модальное окно
// (автоматически привязано к кнопке "Создать голосование")
// Голосование
await chatPoll.vote(pollId, [optionId]);
// Закрытие
await chatPoll.closePoll(pollId);
// Рендер HTML
const html = ChatPoll.renderPoll(pollData, userVotedOptionIds);
```
### CSS стили (chat-polls.css)
- `.poll-widget` - контейнер голосования
- `.poll-question` - вопрос
- `.poll-option` - вариант ответа
- `.poll-option.voted` - выбранный вариант
- `.poll-option-result` - результат с прогресс-баром
- `.poll-footer` - подвал с информацией
## UI/UX
### Меню вложений
В меню прикрепления файлов (bi-paperclip) добавлен пункт:
```html
<li>
<a class="dropdown-item" href="#" id="createPoll">
<i class="bi-bar-chart text-warning me-2"></i>
Создать голосование
</a>
</li>
```
### Модальное окно создания
- Поле ввода вопроса (до 500 символов)
- Динамический список вариантов (2-10)
- Кнопки добавления/удаления вариантов
- Чекбоксы настроек (анонимность, множественный выбор, викторина)
- Выбор правильного ответа (для викторин)
- Таймер автозакрытия (опционально)
### Отображение голосования в сообщении
**До голосования:**
- Вопрос (жирным)
- Кнопки с вариантами ответа
- Информация о настройках
**После голосования:**
- Вопрос
- Прогресс-бары с процентами
- Галочка на выбранном варианте
- Список проголосовавших (если не анонимное)
- Кнопка "Отменить голос" (если не закрыто)
## WebSocket интеграция
### Отправка нового голосования
```python
channel_layer.group_send(f'chat_{chat_id}', {
'type': 'chat_message',
'message': {
'id': message.id,
'content': '📊 Вопрос',
'poll': {
'id': poll.id,
'question': '...',
'options': [...]
}
}
})
```
### Обновление результатов
```python
channel_layer.group_send(f'chat_{chat_id}', {
'type': 'poll_update',
'poll_id': poll.id,
'message_id': message.id,
'results': poll.get_results()
})
```
## Миграция
```bash
cd backend
python manage.py migrate communications
```
Миграция `0019_add_polls.py` создаст:
- Таблицу `communications_poll`
- Таблицу `communications_polloption`
- Таблицу `communications_pollvote`
- Индексы для производительности
- Констрейнт уникальности голоса
## Админка Django
Зарегистрированы модели:
- `PollAdmin` - управление голосованиями
- `PollOptionAdmin` - управление вариантами
- `PollVoteAdmin` - просмотр голосов
Доступно:
- Просмотр всех голосований с фильтрами
- Inline редактирование вариантов
- Просмотр списка проголосовавших
- Ручное закрытие голосований
## Следующие шаги (опционально)
1. **Добавить поддержку пользовательских вариантов**
- Пользователи могут добавлять свои варианты
- Модерация новых вариантов
2. **Уведомления**
- Уведомление о новом голосовании
- Уведомление о закрытии голосования
- Напоминание проголосовать
3. **Статистика**
- Экспорт результатов в CSV/Excel
- Визуализация (графики, диаграммы)
- История голосований
4. **Расширенные настройки**
- Ограничение по ролям (кто может создавать)
- Видимость результатов до закрытия
- Комментарии к вариантам
## Тестирование
### Ручное тестирование:
1. Открыть чат
2. Нажать на скрепку → "Создать голосование"
3. Заполнить форму, добавить варианты
4. Отправить
5. Проголосовать в другом браузере/инкогнито
6. Проверить обновление результатов в реальном времени
### Тестовые сценарии:
- [ ] Создание обычного голосования
- [ ] Создание анонимного голосования
- [ ] Создание с множественным выбором
- [ ] Создание викторины
- [ ] Голосование в открытом голосовании
- [ ] Изменение голоса
- [ ] Закрытие голосования (автором)
- [ ] Автозакрытие по времени
- [ ] Real-time обновление для других пользователей
- [ ] Отображение в мобильной версии
## Troubleshooting
### Голосование не создаётся
- Проверить миграции: `python manage.py migrate`
- Проверить CSRF токен в запросе
- Проверить права пользователя на отправку в чат
### Результаты не обновляются
- Проверить WebSocket соединение (консоль браузера)
- Проверить Channels/Redis настройки
- Проверить channel_layer в consumers.py
### Стили не применяются
- Проверить подключение chat-polls.css в шаблоне
- Очистить staticfiles: `python manage.py collectstatic --clear`
- Проверить загрузку в DevTools → Network
## Файлы изменений
### Backend:
- `communications/models.py` - модели Poll, PollOption, PollVote
- `communications/migrations/0019_add_polls.py` - миграция
- `communications/admin.py` - админка для голосований
- `api/v1/communications/poll_views.py` - API endpoints
- `api/v1/urls.py` - маршруты API
### Frontend:
- `static/js/components/chatPoll.js` - основной модуль
- `static/css/components/chat-polls.css` - стили
- `static/js/chat-detail-enhanced.js` - интеграция
- `templates/communications/chat_detail.html` - UI (модалка, кнопка)
## Производительность
### Оптимизации:
- Индексы на poll, option, voter
- Денормализация vote_count (избегаем COUNT на каждый запрос)
- Денормализация total_voters
- Prefetch для результатов с вариантами и голосами
### Рекомендации:
- Для больших чатов (>1000 участников) кешировать результаты
- Периодически обновлять vote_count через Celery задачу
- Ограничить частоту обновлений WebSocket (throttling)
---
**Автор:** GitHub Copilot
**Дата:** 30 ноября 2025 г.