Як заповнити 100 таймшитов за 2 хвилини

Пост про те, чому наші програмісти тепер заповнюють таймшит не 32, а тільки 2 хвилини і про те, як можна налагодити автоматичний облік робочого часу за рахунок імпорту даних з трекінгових систем TFS, Redmine і Jira на Microsoft Project Server.



Стаття буде цікава менеджерам проектів, керівникам компаній-розробників, а також програмістів, що цікавляться інтеграцією різних систем управління проектами.

Проблема — бардак в заповненні таймшитов
Для 99% компаній-розробників облік робочого часу програмістів потрібен як повітря, щоб рахувати витрати. Тому багато змушують співробітників заповнювати таймшиту (timesheet, свого роду — табель обліку робочого часу). Часто це передбачає заповнення вручну таблиці із завданнями і витраченим на них часом.

99% програмістів, м'яко кажучи, недолюблюють цю процедуру. Ще б, чудова перспектива для п'ятничного вечора — 30 хвилин сидіти і тупо копіпаст всі свої завдання за тиждень з трекинговой програми в таймшит. А, як відомо, коли люди роблять тупу і ненависну роботу, то результат буває так собі. Ось і в нашій компанії половина таймшитов в п'ятничний вечір були далекі від досконалості. У чому це виражалося?

В першу чергу в тому, що таймшиту не відображали всіх «бойових» завдань, які вирішували розробники. Програміст міг написати, що просто всі 40 годин працював за такого проекту. Чи міг завдання з одного проекту помилково віднести в таймшиті до іншого.

Для боротьби з усім цим безладом прекрасна співробітниця з проектного офісу (фотографія якої, безсумнівно, прикрасила б статтю, але публікувати її ми все-таки не будемо) ніжно, але невідступно пресингувала 100 осіб половину робочого дня в понеділок. Потім по довгому чек-листом перевіряла 1200 табличних рядків всіх таймшитов. І тільки до вівторка керівництво мало повну картину.

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

Як було. Процедура збору таймшитов.
Більше трьох днів


Рішення — автоматизація процесу заповнення таймшитов
Рішення проблеми поступово викристалізувалося. Ми міркували так. Якщо програміст всю свою роботу фіксує в трекері, то чому б не брати дані про завдання і кількості робочих годин прямо звідти? Це дозволить швидко отримувати інформацію для аналізу роботи, кадрового документообігу та інших менеджерських завдань. Далі справа була за малим — знайти гарне технологічне рішення по інтеграції наших трекерів з системою, в якій ми враховуємо робочий час (Microsoft Project Server — докладніше про нього тут).

Як стало. Процедура заповнення таймшитов
До 100 штук за 2 хвилини!
Максимальний час збору даних — 2 години 10 хвилин.



Що ми отримали?
В MS Project Server з'явилася чарівна кнопочка, яка дозволяє зробити таймшит за 2 хвилини. Заходиш в п'ятницю в Project Server, він автоматично будує таймшит за даними з трекера за тиждень. Туди залишається додати лише дані про відгули, відпустці, на лікарняному, якщо були. І вуаля — натискаєш кнопку, підтверджуєш, що все правильно, і таймшит готовий.


Треба сказати, що чари працює тільки у тих, хто весь тиждень дотримувався технологію ведення проектів в трекері: фіксував завдання, а також заплановане та фактичне час на їх виконання. Ці гарні хлопці в п'ятницю отримують плюс в карму і автоматичне заповнення таймшита.

Автоматично заповнений таймшит


Бонуси чарівної кнопки
1. Заощадили 200 робочих годин
Якщо раніше у співробітника на заповнення таймшитов йшло 30-40 хвилин, то зараз 2 хвилини. А якщо перерахувати на 100 чоловік в місяць, то ми заощадили близько 200 робочих годин.

Сюди можна додати ще 32 робочих години, які йшли на перевірку правильності заповнення таймшитов, замість яких тепер йде тільки 4 години в місяць. Економія часу пояснюється просто: тепер у таймшиту вивантажуються реальні завдання з трекерів, а не ті, які люди вбили руками.



2. Оперативно отримуємо аналітику по проектам за тиждень
Тепер в 19:00 в п'ятницю повністю заповнені 91% таймшитов, і у менеджерів є аналітика роботи за проектами вже в кінці тижня. Раніше до цього часу було зібрано менше половини.

П'ятниця, 19:00. Кількість повністю заповнених таймшитов


3. Призвели до одноманітності стандарти ведення всіх проектів
Тепер у всіх трекінгові системах всі проекти, клієнти та інші дані позначено одноманітно. Скрізь проставлені цифри щодо запланованого і фактичного часу на виконання завдань. А це зменшує наші ризики і підвищує керованість проектів.

4. Спростили контроль того, як дотримується технологія ведення проектів
Раніше, щоб зрозуміти, як ведеться проект, потрібно було вручну заглянути в трекер. Тепер контроль відбувається автоматично при відправці таймшита. Наприклад, якщо в Redmine не проставлені статуси завдань і час на їх виконання, то ці завдання просто не выгрузятся в таймшит. І навпаки, якщо проектний офіс бачить правильно заповнений таймшит — це підтверджує, що його автор заповнив всі поля трекинговой програмі.

За фактом на виході ми отримали реинжиринг всього бізнес-процесу. Обладнали роботу так, як зручно нам, з нашими великими комплексними проектами.

5. Отримуємо аналітику по будь-яким параметрам вже в п'ятницю
Якщо раніше аналітика за минулий тиждень приходила до менеджерів тільки у вівторок-середу, то тепер вже в кінці поточного тижня, увечері в п'ятницю, 90% інформації з таймшитам зібрано. На основі її збудований OLAP-куб

Скільки багів за тиждень було виправлено? Скільки годин пішло на кожен з проектів? Де виникли вузькі місця і чому? Аналізувати дані тепер можна більш оперативно. Але це не межа. Наступного разу ми розповімо про те, як можна отримувати зведені дані за всіма проектами з трекерів в режимі реального часу за допомогою вдосконаленої версії нашого OLAP-куба.

А зараз подробиці, м'ясо і шматки коду.

Автоматизація бізнес-процесу по кроках
Крок 1. Стандартизували ведення проектів в різних трекінгові системах


Складність завдання полягала в тому, що в компанії для управління різними за технологічної начинки проектами використовуються три різних трекера (системи для управління проектами). Це продиктовано вимогами з точки зору технологій і специфікою самих проектів.

Управління проектами у відділах розробки — спеціалізовані трекінгові системи Облік робочого часу та аналіз бізнес-процесів у компанії
TFS — для відділу технологій Microsoft. Microsoft Project Server — система для управління портфелями проектів
Jira — проектами, в яких замовник теж користується системою Jira.
Redmine — для відділу мобільних технологій, Java-розробників і дизайнерів.
Стандарти ведення проектів в різних трекерах трохи відрізнялися. Приміром, для різних проектів могли бути прийняті різні воркфлоу. Або комплексний проект для одного і того ж замовника міг у відділі мобільного розробки (в Redmine) називатися, умовно, «Мобілка для крутого клієнта N», а у відділі веб-розробки (TFS) «Внутрішній сайт компанії N».

Для інтеграції весь цей зоопарк довелося причесати — забезпечити, щоб у кожній системі однаковим проектів давалося ідентичну назву, скрізь було вказано час виконання завдань і застосовувалася загальна методологія ведення.

На рівні технологій це вимагало прикрутки деяких можливостей до самих трекерам. Наприклад, в TFS немає колонки, в яку можна вписати кількість годин, що ти витратив на завдання в певну дату.

Крок 2. Написали сторінку, яка імпортує запису в MS Project Server
Розповімо, як можна зробити таку сторінку. Намітимо короткий план того, що потрібно:

  1. Додати кнопку в MS Project.
  2. Авторизуватися в трекинговой системі і вважати дані по трудовитратам звідти.
  3. Відобразити отримані дані користувачеві для перевірки.
  4. Записати дані в MS Project.

2.1. Кнопочка.

Давайте розглянемо, як можна розширити інтерфейс MS Project Server. Оскільки, як всім відомо, MS Project Server працює поверх MS SharePoint Server, можна скористатися можливостями останнього, щоб додавати різні елементи інтерфейс MS Project. Зокрема, нам потрібно було, щоб користувач запускав імпорт безпосередньо з сторінки з таймшитом. Для цього в Ribbon таймшита можна додати спеціальну кнопочку і пов'язати її з відповідною дією. Для того щоб мати можливість розгорнути це в MS SharePoint необхідно створити Feature, який містить елемент типу Custom Action.

<CustomAction
Id="EBT.MSP.TimesheetSync.ImportFromRedmine.CustomAction"
Location="CommandUI.Ribbon"
Title="Import data from RedMine" >
...
</CustomAction>

Всередині цього CA визначаємо відповідне розширення.

<CommandUIDefinition Location="Ribbon.ContextualTabs.Timesheet.Home.Sheet.Controls._children">
...
</CommandUIDefinition>

Власне, це досить добре відомо.

Єдина проблема, яка може виникнути — як обчислити згаданий Location. Ми хочемо, щоб кнопка з'явилася тут.



Інформації в інтернеті з цього питання не так багато, тому основним джерелом інформації і є файл pwaribbon. Файл досить великий, але розібратися в ньому можна. Після того як ми знайшли потрібний Location, все просто:

Додаємо в нього кнопку.

<Button Id="EBT.MSP.TimesheetSync.Import.ANCHOR.RedmineButton"
LabelText="Import from Redmine"
Image16by16="/_layouts/15/EBT.MSP.TimesheetSync/img/rm_small.png" 
Command="EBT.MSP.TimesheetSync.Import.ANCHOR.RedmineButton.Command"
TemplateAlias="o1" />

І визначаємо відповідну команду.

<CommandUIHandler
Command="EBT.MSP.TimesheetSync.Import.ANCHOR.JiraButton.Command"
CommandAction="javascript:function ProjectCenterExtension() {
var _grid; // Display surface (a view) of the JS Grid.
var _satellite; // Control wrapper for the JS Grid.
var props = window.timesheetComponent.get_TimesheetSatellite().get_impl()._pageProperties;
window.location = '_layouts/15/EBT.MSP.TimesheetSync/ImportTimesheetData.aspx?ts=' + props.tsUid + '&prd=' + props.prdUid + '&datasource=jira_ebt'; 
}; 
ProjectCenterExtension();"
/>

Як видно з наведеного коду, команда просто перенаправляє користувача на відповідну сторінку типу ApplicationPage. Ця сторінка, яку ми написали самі. Власне всередині неї і відбуваються всі подальші «брудні» справи.

А саме:

  1. Авторизація в трекинговой системі і зчитування даних.
  2. Відображення цих даних на сторінці, для того щоб користувач міг побачити, що додасться в його Timesheet.
  3. Запис інформації в MS Project.

2.2. Читання даних

Тут виникло дві проблеми:

1. Як авторизуватися на трекерах для отримання даних.
2. Як зіставити користувача MS Project з користувачем трекинговой системи.

Глобально тут можна виділити 2 підходи. Забігаючи вперед, скажемо, що нам довелося застосувати обидва підходи для різних трекерів:

  • Завести нікого суперкористувача, який може отримати всі дані з трекера. А далі намагатися співставляти імена користувача MS Project Server і трекерів.
  • «Прокинути» авторизацію з MS Project Server трекер. Тут є очевидний плюс — зіставлення не потрібно, можна просто отримувати запису користувача за його «проброшенной облікового запису. Але з цим пов'язані деякі проблеми, про які трохи пізніше.
Рішення для Redmine і TFS
У нашому конкретному випадку ці трекери «живуть» в нашій інфраструктурі, тому ми вирішили піти першим шляхом.

У разі Redmine все просто — у Redmine є функція Log Work, яка працює рівно так, як потрібно. Тобто людина може зайти в задачу і зазначити, скільки годин він витратив на завдання такого-то числа.



Далі справа техніки — зробити подання в базі даних Redmine (у нас Redmine використовує MySQL), і все готово. Задача вирішена, ми можемо отримати дані.

У випадку з системою TFS все трохи складніше. Такої можливості, як у Redmine, в ній немає. Є певні Add-In'и, які її надають, наприклад, www.tfs-timetracker.com — річ цікава, хоча і недешева.

Але можна підійти до цього завдання з організаційної точки зору. Як? В TFS є поле Completed Work, що розробники повинні записувати сумарно витрачені години на завдання. Шаблони проектів типу Scrum таке поле приховано, але його можна відобразити. Далі з історії змін Work Item можна відстежити зміну цього поля і зрозуміти, коли додавалися годинник, коли, ким і скільки часу було витрачено. Звичайно це вимагає дуже щільної роботи з трекером з боку розробника. Він не може зарепортить час заднім числом, але, тим не менш, якщо все робити вчасно і акуратно, то такий підхід дуже непогано працює. Більш того, він змушує розробників вести себе дисципліновано.

JIRA
У нашому випадку JIRA «хоститься» стороннього оточенні. Тому ні суперкористувача, ні доступу до БД у нас немає. Але зате в JIRA є чудовий REST API, який надає те, що потрібноhttps://docs.atlassian.com/jira/REST/cloud/ ).

Щоб вважати дані, потрібно авторизуватись по протоколу OAuth. Після запуску імпорту з JIRA людина перенаправляється на сторінку авторизації JIRA, авторизується там (якщо користувач ще не авторизований), після чого повертається на сторінку імпорту.

(На жаль JIRA підтримує тільки OAUTH 1, який на поточний момент трохи застарий, але прорватися можна.) Отримавши OAUTH token ми:

1. Витягуємо ім'я автора методом

api/v2/myself

І таким чином ніякого зіставлення імен не треба, ми просто беремо користувача, який залогінився в JIRA.

2. Далі шукаємо всі завдання, для яких користувач залогировал час в певний період.

api/v2/myself/search?jql=worklogDate>={start_date} and worklogDate<{end_date} and timespent>0 and worklogAuthor={author}&fields=summary,project,parent,timeoriginalestimate,{pswa}&startAt=0

На перший погляд, це все, що потрібно, але не зовсім. MS Project підтримує роботу в режимі делегата, коли людина щось робить від імені іншого, і наші менеджери активно цим користуються. Тут, звичайно, трапляються певні колізії, оскільки, якщо людина A уособлює в MS Project людини Б (зазвичай керівник — підлеглий, або відповідальна особа — співробітника), то в JIRA він його не уособлює, а залишається самим собою. І подібним запитом він отримає завдання, призначені на нього самого, тобто людини, які запишуться в timesheet людині Б. Більш того, на жаль, немає методу (або ми не знайшли його), щоб отримати всі завдання по всіх проектах, в яких зазначений користувач залогировал свої роботи. І це ще не все — людина А може зовсім не мати у JIRA відповідних повноважень на читання потрібних завдань. Втім, останнє питання можна вирішити організаційно.

Для того щоб перебрати всі завдання, доводиться спочатку отримати всі проекти методом:

api/v2/project

Далі пробігтися по всіх проектах і дістати всі завдання.

api/v2/search?jql=project={projectKey}&fields=summary,project,parent,timeoriginalestimate,{pswa}&startAt=0

Для кожної знайденої завдання отримати залогированое час і вибрати те, що потрапляє в потрібний період і належать потрібного авторові.

api/v2/issue/{id}/worklog

У цьому випадку проблема зіставлення імен знову, звичайно, весело зустрічає нас. Крім того, такий процес займає дуже багато часу. Набагато краще, напевно, тут і не придумати, втім, якщо є краще рішення, були б раді почути.

2.3. Відображення даних.

Тут особливо розповісти нічого — код на старому доброму ASP.Net, який розмальовує отримані дані.

2.4. Запис даних у Timesheet

Отже, ми перейшли на спеціальну сторінку, авторизувалися, отримали потрібні записи, залишилося записати їх в MS Project. Насправді в MS Project є кілька видів API, але, оскільки ми працюємо всередині нього, ми використовували той же самий, що використовують сторінки самого Project'a. API цей виглядає, скажімо так, незвично.

Наприклад, щоб вважати ресурс, потрібно викликати метод.

var dataset = resourceSvc.ReadResource(userId);

І це цілком зрозуміло, але потім доведеться провести ще багато часу, намагаючись зрозуміти, де в цьому DataSet потрібна табличка, рядок і колонка. Документації з цього питання досить мало і, чесно кажучи, жодного працюючого з цим API приклад додавання Timesheet Lines в Timesheet ми в інтернеті не знайшли. Але, поковырявшись в «потрухах» MS Project Server за допомогою ILSPY, ми врешті-решт написали наступний код:

private void ReportTime(Guid timeSheetId, List<TaskData> tasks, Guid projectId)
{
var timesheetDs = _timeSheetSvc.ReadTimesheet(timeSheetId);

TimesheetDataSet.LinesRow[] rows = (TimesheetDataSet.LinesRow[])timesheetDs.Lines.Select();

bool needUpdate = false;

foreach (var task tasks in)
{
if (!task.Estimation.HasValue && !task.IgnoreEstimation) continue;

Guid taskId = new Guid(task.Id);
Guid assnId = new Guid(task.AssnId);
bool isRowExist = false;
foreach (var row in rows)
{
if (row.ASSN_UID != assnId) continue;
isRowExist = true;
AddActual(timesheetDs, task, row); 
}

if (!isRowExist)
{
TimesheetDataSet.LinesRow line = timesheetDs.Lines.NewLinesRow();
line.TS_UID = timeSheetId;
line.TASK_UID = taskId;
line.PROJ_UID = projectId;
line.ASSN_UID = assnId;
line.TS_LINE_CACHED_ASSIGN_NAME = task.Name;
line.TS_LINE_UID = Guid.NewGuid();
line.TS_LINE_CLASS_UID = PSLibrary.TimesheetConst.const_StandardLineClassGuid;
line.TS_LINE_STATUS = (byte)LineStatus.Pending;
line.TS_LINE_VALIDATION_TYPE = (byte)ValidationType.Verified; 
timesheetDs.Lines.AddLinesRow(line);

_timeSheetSvc.PrepareTimesheetLine(timeSheetId, ref timesheetDs, new Guid[] { line.TS_LINE_UID });
AddActual(timesheetDs, task, line); 
}
needUpdate = true;
}

if (needUpdate)
{
Guid jobUid = Guid.NewGuid();
_timeSheetSvc.QueueUpdateTimesheet(jobUid, timeSheetId, timesheetDs.GetChanges() as TimesheetDataSet);

TaskUtils.WaitForQueue(_queueSvc, jobUid);
} 
}

Цей код, власне, і записує потрібні дані в MS SharePoint. Чесно кажучи, проблем при роботі з MS Project було найбільше. Про це можна написати цілу окрему статтю.

Крок 3. Відкатали технологію на тестовій групі користувачів.
Ми впроваджували «чарівну кнопку» окремо для кожного трекера, кожен раз охоплюючи по ~30% співробітників, і щоразу перед повноцінним запуском тренувалися на невеликій групі з 3-5 «піддослідних» користувачів, з якими в спокійній обстановці проходили весь процес і доводили його до production ready стану, щоб не отримати в п'ятницю ввечері шквал запитань і обурення відразу від 30 осіб.



Крок 4. Зібрали зворотний зв'язок
Що кажуть співробітники EastBanc Technologies про «чарівної кнопки» для заповнення таймшитов


Юрій Булкін, головний Java-архітектор

Вогонь!!! Коли я перший раз натиснув кнопку і тут же побачив повністю заповнений таймшит — я просто очам не повірив. Мені багато разів говорили, що все буде відбуватися так. І я розумів, як це має виглядати. Але коли я сам отримав автоматично заповнений двадцятьма завданнями таймшит, це було так круто, що я тільки тоді усвідомив усе зручність цієї штуки.

Олександра Очеретинская, керівник проектного офісу.

Ми зрозуміли: те, що ми в п'ятницю змушуємо програмістів заповнювати таймшиту — це неполезная, неінтелектуальна діяльність. Ми вимагаємо вручну вносити інформацію, яка вже існує, просто в іншому місці і в іншому вигляді. І ми можемо позбавити людей від цієї непотрібної роботи. Якщо люди в будь-якому випадку вже працюють за завданнями, які заведені в трекерах, то чому б просто не вивантажувати цю інформацію для обліку? Так ми і зробили.

Максим Подус, інженер-програміст

Набагато швидше став процес заповнення таймшитов, хвилин на 30… Раніше було 32 хвилини, а зараз всього 2 хвилини.

Василь Лебедєв, провідний інженер-програміст.

Класно! Менше дублирующейся роботи, більша деталізація тасков. Дивишся, і таймшиту вчасно заповнювати почну. :)

Ірина Мананникова, керівник проектів

Конкретно у своїй роботі я позбавилася від одноманітної, рутинної ручної процесу щотижневої перевірки таймшитов (роботи). Швидкість заповнення таймшитов — це приємний бонус. А головною метою було — оперативно отримувати прозору і зрозумілу картину за проектами в трекерах. Мета досягнута, в кінці кожного тижня у нас є практично на 100% актуальна інформація по кожному проекту за підсумками минулого тижня.

Крок 5. Profit!
Насолоджуємося життям і ефективно використовуємо вільний час.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.