Запланировали задачу в MS Project, а исполнители не в курсе, т.к. смотрят свои таски в JIRA? Вручную заводить в таски долго, а любое перепланирование вызывает проблемы?
Если вы сталкивались с этим, мой опыт может показаться полезным.
Вводные
Привет, меня зовут Алексей. Сейчас я заместитель руководителя ЦК Проектирования и Архитектуры одной из информационных систем Россельхозбанка. Но рассказать я хочу про свой предыдущий опыт.
Работал я какое-то время назад заместителем руководителя одного отдела. И этот достаточно большой отдел, занимался развитием одной большой информационной системы. Большой отдел — это примерно 90-100 аналитиков и разработчиков. При этом, в связи с нехваткой менеджерского состава, управлять задачами такой команды приходилось 2-3 менеджерам.
Проводя собеседования кандидатов на вакансии таскменеджеров в нашу команду, я часто спрашивал, как у них реализовано планирование и назначение задач на исполнителей. И наиболее часто ответ звучал так: «планируем в проджекте, потом руками создаем задачи в JIRA» или «Ой, мы так и не смогли победить интеграцию проджекта и JIRA». Вдобавок и бывшие коллеги, перешедшие работать в другие организации, часто стали спрашивать, как же мы подружили MS Project и Jira. Именно это и побудило написать данную статью.
Т.к. в рамках отдела нас интересовало больше ресурсное планирование выполняемых задач, мы не пытались сделать ведение общих планов по задачам в своем project-файле. Да, бывают задачи, в рамках которых дорабатываются смежные информационные системы в других отделах, но цели вести календарное планирование таких задач перед нами не стояло. Но стоит понимать, наш ресурсный план – фактически часть календарного плана мультисистемной задачи, или календарный план по моносистемной задаче.
Вдобавок ко всему, исполнители не были разбиты на группы и вести учет всех ресурсов приходилось в одном project-файле (ms project server у нас не было). В огромном файле с 6000-6500 строк.
Основной системой для учета задач в нашей организации ранее была внедрена Atlassian Jira. Первоначально связку JIRA+MSProject внедрил мой предшественник, за что я ему очень благодарен.
Количество уже запланированных задач в моменте могло достигать 300-350 штук. Ежемесячно поступало на планирование ещё 20-30 задач. Задачи, планируемые по методологии Waterfall, фактически генерировали еще по 10-15 подзадач. Создавать этот объем в JIRA вручную очень трудоемко. Не создавать задачи в JIRA – тоже плохая идея. Гарантировано донести до исполнителей какие задачи в какой срок нужно сделать – практические нереально, учитывая количество сотрудников.
Изобретая велосипед
Первоначальное решение по интеграции MSProject <-> JIRA было реализовано в виде набора макросов, которые вызывали внешний шлюз к JIRA. Исходных кодов у нас не было, поэтому шлюз был черным ящиком, который мы не могли изменять. Шлюз взаимодействовал с JIRA через SOAP и после очередного обновления JIRA благополучно перестал работать.
В этот момент стало ясно, что необходимо искать новое решение, переходить на ручное управление задачами в JIRA не хотелось никому (напомню про количество задач и исполнителей). На поиск готовых решений времени не нашлось, поэтому стали изобретать велосипед.
Почему «велосипед» - так потому что ранее все работало, пусть и по другому протоколу. Да и вероятно существуют другие готовые решения, которые я не нашел.
В качестве «велосипеда» стали переделывать взаимодействие черещ RestApi, при этом отказаться от внешнего шлюза, в пользу реализации на VBA.
Плюсы реализации на VBA:
Открытый и достаточно простой код, который оперативно можно исправить
Отсутствие необходимости установки дополнительных сред разработки.
Пересборка dll шлюза требовала регистрацию ее в системе. Учитывая, что админских прав на рабочей станции у нас нет, исполнение запроса на обновление dll-библиотеки шлюза могло занять неделю. Простой в интеграции MSProject <-> JIRA был для нас недопустим.
Отсутствие необходимости дополнительного ПО, достаточно стандартного набора библиотек.
Минусы реализации на VBA:
Более низкая скорость работы в сравнении с шлюзом, написанным на C# (было принято не критичным, даже учитывая объем задач)
Открытый код может повлечь локальные правки, которые в дальнейшем будет сложнее сопровождать (в связи с количеством пользователей решения – не критично)
Нельзя заложить гарантированные ограничения в код, т.к. код открыт (в связи с количеством пользователей решения – не критично)
Ограничения языка VBA (решается костылями)
В результате был реализован модуль на VBA, который на начальном этапе позволил повторить все функции первоначально решения, а в последствии добавил новых возможностей.
Реализация
Далее представлен набор базовых элементов, из которых можно собрать свой модуль для интеграции с JIRA
Минимально необходимый макрос для создания задачи в JIRA
Sub PostToJIRA
Dim xmlHttpRequest As New MSXML2.XMLHTTP60
Dim sJSON As String '<---------- здесь должен быть JSON с параметрами создаваемой задачи
Dim sConnectionString As String '<---------- здесь должна быть строка подключения к вашей JIRA
Dim sUserandPassword as String '<--------- здесь должна быть логин и пароль, формат User:Password в Base64
With xmlHttpRequest
URL sConnectionString + "rest/api/2/issue/"
.Open "POST", URL, True
.setRequestHeader "Authorization", "Basic " & sUserandPassword
.setRequestHeader "Content-Type", "application/json"
.setRequestHeader "Accept", "application/json"
.setRequestHeader "Origin", sConnectionString
.send (sJSON)
End With
With xmlHttpRequest
While Not .readyState = 4 '<---------- ждем завершения обработки запроса, если не ждать то при попытке обратиться к .responseText свалимся в ошибку
Pause (1)
Wend
sStatus = .Status & " | " & .statusText & " | " & .responseText
sRestAntwort = .responseText
End With
sStatusCode = xmlHttpRequest.Status
If sStatusCode <> "201" Then
MsgBox (sStatus)
eErr = sStatus
End If
Set xmlHttpRequest = Nothing
End Sub
Естественно получение переменных необходимо вынести в отдельные функции. В частности, в нашей реализации строка sConnectionString определяется из конфигурационного файла, как и необходимые атрибуты для Json. А еще правильнее делать не процедуру PostToJira, а функцию, которая будет возвращать ответ от JIRA для дальнейшей обработки.
При создании задачи в JIRA необходимо учитывать следующее:
У пользователя должны быть права на создание задачи в указанном проекте
Все обязательные поля, для данной задачи, должны быть переданы в JSON запросе
Все передаваемые поля должны быть на экранной форме создания запроса в JIRA через браузер
Исполнитель, на которого назначается задача должен обладать правами «быть назначенным на задачу» (самый простой способ проверить – попробуйте назначить исполнителя через браузер)
Пример минимально необходимого JSON для создания задачи:
{"fields":{"project":{"key": "Наш проект"},"summary": "Название задачи","description":"","issuetype":{"id":"Тип задачи в проекте"},"duedate":"2021-05-11",,"assignee":{"name":"ЛогинИсполнителя"}}}
С созданием задач разобрались. Теперь логичное желание каким-то образом изменить задачу в JIRA. Для этого нам нужно в момент создания задачи в JIRA, сохранить в Project идентификатор созданной задачи. Хорошо, что в случае успешного выполнения запроса в xmlHttpRequest.responseText будет передан ответный JSON. Разобрать JSON поможет JSONConverter, который можно взять здесь.
Для обновления задачи нужно немного модифицировать предыдущую процедуру, т.к. используется метод PUT, а не POST. В примере сразу реализовано в виде функции. Константы получаются из конфигурационного файла функциями, а не забиты в переменные, как в предыдущем примере
Private Function PutToJira(iJiraTask As String, iJSON As String) As String
Dim xmlHttpRequest As New MSXML2.XMLHTTP60
Dim tstr As String
With xmlHttpRequest
URL = GetConnectionString() + "rest/api/2/issue/" + iJiraTask
.Open "PUT", URL, True
.setRequestHeader "Authorization", "Basic " & GetUserAndPassword()
.setRequestHeader "Content-Type", "application/json"
.setRequestHeader "Accept", "application/json"
.setRequestHeader "Origin", GetConnectionString()
.send (iJSON)
End With
With xmlHttpRequest
While Not .readyState = 4 '<---------- wait
Pause (1)
Wend
sStatus = .Status & " | " & .statusText & " | " & .responseText
sRestAntwort = .responseText
End With
sStatusCode = xmlHttpRequest.Status
If sStatusCode <> "1223" Then MsgBox (sStatus)
Set xmlHttpRequest = Nothing
PutToJira = sRestAntwort
End Function
Пример JSON для изменения задачи:
{"fields":{"summary": "Название","description":"Описание",,"duedate":"срок исполнения","assignee":{"name":"Логин исполнителя"}}}
Но только выгружать задачи в JIRA может показаться мало (по крайней мере мне так показалось). Поэтому пригодится функция, которая позволит нам получить данные из JIRA.
Пример функции для получения информации по задаче из JIRA:
Private Function GetFromJira(iJiraTasks As String) As String
Dim xmlHttpRequest As Object
Dim sRestAntwort As String
Set xmlHttpRequest = CreateObject("MSXML2.XMLHTTP")
With xmlHttpRequest
URL = GetConnectionString() + "rest/api/2/search?jql=" + iJiraTasks + "&maxResults=500&order by ID"
.Open "GET", URL, False
.setRequestHeader "Authorization", "Basic " & GetUserAndPassword()
.setRequestHeader "Content-Type", "application/json"
.setRequestHeader "Accept", "application/json"
.send
sRestAntwort = .responseText
sStatus = .Status & " | " & .statusText & " "
sStatusCode = .Status
End With
If sStatusCode <> 200 Then MsgBox (sStatus)
Set xmlHttpRequest = Nothing
GetFromJira = sRestAntwort
End Function
В параметр iJiraTask можно передать строку для поиска в jql. Например, запрос “(ID=JIRATASK-1)” вернет выборку, содержащую одну задачу, но если передать условия для поиска – вернутся все задачи удовлетворяющие условиям. Так же можно обратиться напрямую к одной задаче заменив строку «rest/api/2/search?jql=" + iJiraTasks + "&maxResults=500&order by ID"» на «rest/api/2/issue/" + iJiraTask» и в переменной iJiraTask указав ID задачи.
Приведенная выше функция вернет JSON содержащий всю информацию о задаче. Разобрать JSON можно с помощью того же JSONConverter из примера выше. Например, у нас в проекте есть поле «Процент выполнения», это кастомное поле JIRA, для того чтобы получить его значение достаточно выполнить следующий код
sRestAntwort = GetFromJira("(id = " + JiraID + ")")
Set parsed = ParseJson(sRestAntwort)
resA = parsed("issues")(1)("fields")("customfield_14902")
If IsNull(resA) Then resA = "0"
Таким образом, используя приведенные выше примеры функций можно реализовать взаимодействие между MSProject и Jira без использования внешних шлюзов.
Применительно к используемой нами реализации было сделано следующее:
-
Реализовано создание новых задач, с набором дополнительных атрибутов:
Дата начала
Дата окончания
Тип выполняемых работ
Информационная система (в какой-то момент у нас в отделе было несколько дорабатываемых информационных систем)
Количество запланированных Ч/Ч на задачу
Создаются связи с вышестоящей задачей
Реализовано изменение задач в JIRA по запросу из MS Project
Обновление статуса задачи, процента выполнения по задаче в MS Project по запросу в JIRA
Макрос массового обновления всех не завершенных задач в MS Project по запросу в JIRA
В результате процесс работы с задачами приобрел следующий вид:
Поступает запрос на планирование задачи
-
В MS Project создается план задачи. Пример плана:
После утверждения плана задачи выгружаются в JIRA.
Исполнитель берет задачу в работу, и меняет статус, процент выполнения задачи.
Статус и процент выполнения синхронизируется в MS Project на основании информации из JIRA.
Если задача не может быть выполнена в срок, либо требуется перепланирование задачи – исполнитель заполняет специальное поле в задаче в JIRA, на основании которого принимается решение о перепланировании.
При необходимости задача перепланируется в MS Project и изменения выгружаются в JIRA.
При строгой исполнительской дисциплине, подобный подход позволил нам оперативно управлять достаточно большим портфелем задач.
С течением времени мы все-таки смогли выделить внутри отдела команды и найти им тимлидеров, что позволило распределить нагрузку по планированию и контролю за задачами. Теперь этот механизм применяется всеми тимлидами наших команд, которые все ещё работают по waterfall, а не agile.
P.S. Сейчас я уже в меньшей степени занимаюсь планированием. Когда-то давно мне казалось, что процесс планирования и управления задачами достаточно простая история (практика показала, что это далеко не так). Но при росте количества задач и исполнителей, все сильно усложняется.
Как у вас реализован подход к постановке задач исполнителям?