# Проблемы
* Ошибки при разборе фидбека
  * у сдельщиков есть лёгкий доступ к фидбеку, на который не хватает компетенций
* Дисбаланс задач у сотрудников
  * т.к. люди разбирают назначенные не им задачи
* Читерство
  * из-за доступа к неограниченному набору задач
* Информация «у кого какие есть компетенции» хранится только в голове у менеджера
  * сейчас есть ~200 сотрудников. При назначении задач менеджер вручную собирает паззл из логинов и скиллов
* Информация о расписаниях людей и покрытии работ людьми хранится в xls у менеджера
  * __Пример__: фидбек в Москве надо разбирать в среднем двумя людьми. Вася работает 2 через 2, а Петя с выходными суббота и воскресенье. За пересечениями выходных Васи и Пети, а также назначением третьего человека в дни, когда у одного из них выходной, приходится следить вручную.

### Задачи по приоритетам
1. **Первый приоритет:**
   * закрыть доступ к «чужому» фидбеку для разбора
   * сделать для сдельщика фид задач: он выбирает пресет + зону и видит фид соответствующих задач
2. **Второй приоритет:**
   * перенести знание о навыках сдельщиков в НК
   * технически учитывать это знание о навыках при выдаче ролей
3. **Третий приоритет:**
   * перенести управление расписанием из xls в интерфейс поверх базы данных

# Интерфейсное решение только для фидбека

#### **Для менеджера**
* ~~менеджер назначает (пресет + зона + расписание) на пользователя/группу~~
* в первой версии менеджер назначает специальную привязанную к пресету роль на пользователя/группу в интерфейсе acl, для роли задаёт зону и опционально расписание
  * у пользователей появляется доступ к фидбеку из пресета в нужной зоне с заданным расписанием. Технически расписание и зона сохраняются в `schedule_policy`
  * список возможностей:
    * выдать пресет + зона на группу
    * выдать пресет + зона на конкретного пользователя
    * выдать пресет + зона + расписание на группу
    * выдать пресет + зона + расписание на конкретного пользователя
* ~~можно снять (пресет + зона) с пользователя/группы~~
* в первой версии менеджер снимает роль с зоной и расписанием с пользователя/группы в интерфейсе acl 
  * у пользователей пропадает доступ к фидбеку из пресета в нужной зоне
  * список возможностей аналогичен списку выше
  * если выдать пресет1 + зона1 на группу, в которую входит пользователь1, потом выдать пресет1 + зона1 явно на пользователя1, потом отобрать пресет1 + зона1 у пользователя1, то задачи из пресета1 в зоне1 ему видны, потому что они остались назначенными через группу

#### **Для менеджера - доделки на следующих этапах**
* посмотреть список пользователей/групп, назначенных на пресет
  * видны сразу с зонами и расписаниями
  * можно сделать фильтр по зоне при просмотре
* посмотреть количество пользователей, назначенных на пресет в заданной зоне в заданный момент времени
* посмотреть список пресетов, назначенных на пользователя
  * видны сразу с зонами и расписаниями
  * можно сделать фильтр по зоне при просмотре

#### **Для исполнителя:**
  * исполнитель видит фид фидбека для выбранного из доступных пресета, т.е. видит фид своих задач
    * пресет + зону пользователь явно выбирает из назначенных
  * при отображении пинов на карте отображаются только пины, которые попадают хотя бы в 1 назначенный на пользователя пресет

# Техническое решение
Делаем только на основе social. Дополнительные сервисы не заводим, выдачу заданий на другие типы не обобщаем.

#### **Преимущества**
* быстрее, чем городить отдельную систему
* точно будет работать

#### **Недостатки**
* acl-роли используются костыльно
* проверка прав фидбека расплывчато разнесена на 2 сервиса: для фидбека только в social, для остального - в acl
  * но вообще, для зон и сейчас так работает
* не обобщаем систему на выдачу любых типов задач

### Вводные

Сейчас для фидбека существуют пресеты. Пресет суть фильтр по каким-то параметрам. Пресеты хранятся в бд `mapspro_social` в `social.feedback_preset`.
Сейчас у него есть поля:
1. `id`
2. `name`
3. `types` – список типов фидбека
4. `workflows` – deprecated
5. `sources` - список источников
6. `age_types` 
7. `hidden` - «скрытый» фидбек
8. `status`

Поля 3-8 служат фильтрами: при выборе в интерфейсе какого-то пресета отображается только фидбек, удовлетворяющий всем фильтрам. Сейчас для типов фидбека, `age_type` и `hidden` есть permission-ы в acl. На отдельные источники permission-ов нет и заводить их нецелесообразно: источников очень много и они могут меняться динамически, а permission-ы завязаны на выкатку релиза.

### Как будут настроены новые права

1. Проверка прав на фидбек происходит на уровне social, acl их только хранит, но не проверяет. **В social новая проверка прав добавляется во ВСЕ endpoint-ы**
2. каждому пресету ставим в соответствие acl-роль.
   * технически в ней нет никаких permission-ов
3. На роль могут быть навешены зона и расписание. Зона и расписание - это сущности policy и schedule в acl. Расписания проверяются на уровне acl
4. Как происходит проверка прав на фидбек: 
   * смотрим список ролей пользователя, join-им их со списком привязанных к пресетам ролей
   * для каждой из ролей проверяем, входит ли фидбек в соответствующий пресет; если не входит ни в одну, то считаем фидбек недоступным
   * при запросе пинов идём с другой стороны: по ролям пользователя ищем доступные пресеты, по которым строим фильтр, с которым идём в бд, отдельно фильтруем фидбек в тайлах на границах зон по доступным зонам
5. Нужно обеспечить права для внешних пользователей aka common и менеджеров с правами администратора: для них создаём специальные пресеты, в которые входит весь фидбек и назначаем эти пресеты на всех

**Нюанс**: для новых технических пресетных ролей не делаем никакую отдельную защиту от изменения/редактирования. Если кто-то удалит эту роль в acl, то права на пресет перестанут работать: для пользователя всё станет недоступно. Отдельная задача на будущее - помечать такие роли «служебными» и не позволять их модифицировать из ui. Пока не делаем, т.к. пока не было прецедентов с явным вредительством и изменением ролей, которые не нужно трогать

#### Как обеспечить наличие у всех менеджеров прав на выдачу ролей по пресетам
1. заводим специальную роль `feedback_preset_manager`
2. она позволяет назначать привязанные к пресетами роли:
   * при создании пресета и создании привязанной к нему роли автоматически обновляем `feedback_preset_manager` и добавляем в неё доступ к новой роли
   * при удалении пресета и удалении привязанной к нему роли автоматически обновляем `feedback_preset_manager` и удаляем из неё устаревшую роль
3. раздаём эту роль всем менеджерам сдельщиков (или добавляем всех менеджеров в специальную группу, у которой есть эта роль)


### Новые таблицы в базе

##### **preset_roles**
Соответствие между пресетами и ролями: каждому пресету задач соответствует acl-роль.
Роль создаётся автоматически при создании пресета, для существующих пресетов роли надо разово создать скриптом. 

| **field** | **type** | **meaning**
---- | ---- | --- |
`preset_id` | `bigint`| |
`role_id` | `bigint` | id acl-роли, которая соответствует фидбеку из пресета


## API
### Список вопросов
1. ~~json или protobuf?~~ Договорились, что json: не делаем в существующем сервисе протоколы в разных форматах
2. пока что надо поревьюить концепцию, все протоколы будут в следующей версии. Ниже в примерах наброски общего характера

**Disclaimer**: все запросы от пользователей приходят с user-тикетами. Т.е. для каждого запроса известен uid пользователя, по нему проверяются права. Api для запросов не от пользователей будет жить в social-backoffice

### Для использования менеджерами

<details>
<summary><b>Api для assign/unassign, необоходимое для нового ui. На первом этапе с назначением ролей через acl можно обойтись без него</b></summary>

#### **feedback/presets/assign/group**
**Method**   | PUT |
-------------|-|
**Params**   | |
`preset_id` | Required
`group_id` | Required
`zone_id` | Required
`schedule` | Optional, string - расписание в формате acl
**Description** | Назначить на группу пресет задач в конкретной зоне картографирования с заданным расписанием. Технически это получение по таблице `preset_roles` связанной с пресетом роли и назначение роли c зоной и расписанием на группу в acl
**Response code** | 200


###### Детали реализации
* При назначении пресета автоматика раздаёт соответствующую пресета роль в нужной зоне с заданным расписанием всем пользователям из группы `group_id`/ пользователю `uid`. Зона и расписание хранятся в acl
* Нельзя назначить пресет без привязанной роли: бекенд выдаст ошибку
* При `unassign` автоматика отбирает роль у пользователей из `group_id` в нужной зоне, если та же роль не назначена пользователю через другую группу/напрямую
* зона и расписание хранятся на уровне acl:
   * в таблице `acl.schedule_policy` хранится `(role_id, agent_id, zone_id, schedule_id)`
   * в таблице `acl.policy` хранится `(role_id, agent_id, zone_id)`

#### **feedback/presets/assign/user**
**Method**   | PUT |
-------------|-|
**Params**   | |
`preset_id` | Required
`uid` | Required
`zone_id` | Required
`schedule` | Optional, string - расписание в формате acl
**Description** | Назначить на пользователя пресет в заданной зоне с заданным расписанием.
**Response code** | 200



#### **feedback/presets/unassign/group**
**Method**   | DELETE |
-------------|-|
**Params**   | |
`preset_id` | Required
`group_id` | Required
`zone_id` | Required
**Description** | Удалить для группы пресет в заданной зоне. Технически это получение по таблице `preset_roles` связанной с пресетом роли и удаление этой роли c зоной у группы в acl
**Response code** | 200


#### **feedback/presets/unassign/user**
**Method**   | DELETE |
-------------|-|
**Params**   | |
`preset_id` | Required
`uid` | Required
`zone_id` | Required
**Description** | Удалить для пользователя пресет в заданой зоне
**Response code** | 200


#### **feedback/presets/users**

**Method**   | GET |
|-------------|-|
**Params**   | |
`preset_id` | Required
`view_policy` | Optional, enum: `flat`, `show`, `no`. по умолчанию `show`. Показать всех пользователей, которым назначен пресет. `flat` - плоским списком, `no` - не показывать пользователей, которые назначены через группы, `show` - показать список пользователей, назначенных не через группы, и список групп
`zone_ids` | Optional, список зон через запятую
**Description** | Получить список пользователей, на которых назначен пресет в каком-то подмножестве заданных в запросе зон. В процессе реализации надо подумать над расписаниями: можно добавить timestamp в запрос, чтобы посмотреть кому назначен пресет в конкретный момент времени; можно отдавать пользователей вместе с расписаниями. Технически - это запрос в acl
**Response code** | 200

На первом этапе смотреть список пользователей можно по роли через интерфейс acl и не делать отдельный endpoint

#### **feedback/presets/user_presets**

**Method**   | GET |
|-------------|-|
**Params**   | |
`uid` or `group_id`| Required
**Description** | Получить список пресетов, назначенных заданному пользователю. Технически смотрим acl-роли пользователя и join-им их с ролями, которые привязаны к пресетам
**Response code** | 200

На первом этапе можно смотреть список ролей пользователя в интерфейсе acl и не делать отдельный endpoint
</details>

---------

В ответ ручки [`GET /feedback/presets`](https://a.yandex-team.ru/arc/trunk/arcadia/maps/wikimap/mapspro/services/social/src/api/feedback/presets_api.cpp?rev=r8834277#L62) надо добавить id роли для каждого пресета. Возможно, сделать отдельный endpoint `GET /feedback/presets?preset_id=...` с получением информации про конкретный пресет и привязанной к нему роли.

Возможно, в [`PUT /feedback/tasks/regions`](https://a.yandex-team.ru/arc/trunk/arcadia/maps/wikimap/mapspro/services/social/src/api/feedback/regions_api.cpp?rev=r8834277#L136) надо сделать возможность задавать параметр `preset_id`, чтобы сразу получать счётчик задач по пресету. Т.е. для того же формата ответа сделать endpoint `GET /feedback/tasks/regions?preset_id=...&zone_id=...`.


#### Сценарий «получить список пользователей, которым назначен пресет»
   * по id пресета фронт получает id привязанной роли
      * если очень хочется, роль можно искать по названию, но тут никаких гарантий, что не будет других ролей, которые тоже сматчатся по названию
   * по id роли через интерфейс acl получает список обладателей роли

### Для использования сдельщиками

#### **feedback/presets/user_presets**

**Method**   | GET |
|-------------|-|
**Description** | Получить список назначенных пользователю пресетов: в ответе отдаются пресеты с зонами и расписаниями. Технически смотрим acl-роли пользователя и join-им их с ролями, которые привязаны к пресетам, отдаём список найденных пресетов
**Response code** | 200

#### **feedback/presets/user_feed**

**Method**   | GET |
|-------------|-|
`preset_id` | required
`zone_id` | optional
**Description** | Получить список назначенных пользователю задач из заданного пресета в заданной зоне. Если фильтр по зоне не задан, то в фид входят задачи из всех назначенных пользователю зон для этого пресета. Расписания роли учитываются на уровне acl
**Response code** | 200

#### feedback/pins

**Method**   | PUT |
|-------------|-|
`preset_id` | required
`x`,`y`,`z` | required - tile аналогично [существующему](https://a.yandex-team.ru/arc/trunk/arcadia/maps/wikimap/mapspro/services/social/src/api/feedback/pins_api.cpp?rev=r8834277#L134) endpoint-у
`zone_id` | optional
`status` | optioinal - статус фидбека, с которым показывать пины: доп. фильтр для статусов, которые доступны внутри пресета
**Description** | Получить пины фидбека из заданного пресета в заданной зоне. Если фильтр по зоне не задан, то отображаются все пины пресета. При запросе пинов учитываются доступы пользователя: если у него нет прав на запрошенный пресет в заданный момент времени, то он получит ошибку в ответ.
**Response code** | 200
