Я — Android-разработчик, и мне не нравилось вести ворклоги вручную

    Когда я устраивалась в Skyeng, солнце светило чуть ярче, трава зеленее не была (шла такая же ранняя весна), а тимлид попросил записывать в Jira, сколько времени ушло на кодинг, а сколько на разговоры и ревью. Хотя бы раз в две недели.


    «По этим данным мы пробуем понять, надо ли корректировать эстимейты и нет ли проблем в коммуникации в команде», — говорили они. А вот кто такой «бабайка», так и не рассказали..

    Поскольку мы все удалёнщики, идея звучала разумно. Да и мне стало интересно, куда девались эти восемь часов: вот прошли, но за чем именно? Однако логировать было непривычно. И вообще лень. Тогда я решила поискать что-нибудь, что будет вести ворклоги за меня. А в процессе исследования немного увлеклась и написала свой плагин для IntelliJ IDEA.

    Ниже вы найдете субъективный обзор готовых инструментов и мой велосипед (с исходниками).

    Изучаю решения, которые использует команда


    Первое, что я сделала — пошла узнавать у коллег, кто чем пользуется. Варианты подобрались примерно следующие:

    1. Clockify — одна кнопка, чтоб править всем


    Затрекать время на кодревью или написание доки? Плагин добавит кнопку (довольно ненавязчивого серого пакмена) почти куда угодно.


    Так смотрится в Google Docs


    И на GitHub

    Натреканным можно полюбоваться на дашборде, а можно импортировать в таблицу и закинуть в Jira.


    Интеграция в Jira поставляется отдельно, тоже браузерным расширением

    Впрочем, есть возможность загрузить в свой рабочий календарь таблицу с ворклогами, полученную из Clockify. А для учёта времени в оффлайне есть десктопные приложения под windows, linux и mac. Мобильные приложения — тоже. Основной функционал бесплатен, но есть и несколько тарифных планов с плюшками вроде приватности, напоминалок и темплейтов для проектов.

    2. Toggl, просто Toggl


    Всё примерно то же самое, но плюшек чуть побольше — например, в десктопном приложении можно поставить себе напоминалки о логировании времени, включить режим pomodoro, есть даже возможность увязать определённое приложение с определённым проектом и настроить автотрекинг.


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

    3. Toggl + скрипт на Python: когда просто кнопки уже недостаточно


    Изобретение моего коллеги. Как-то он решил сократить телодвижения по экспорту таблиц в Jira и переложил выгрузку налогированного в Toggl на скрипт. Можно положить в cron и радоваться жизни.

    4. Wakatime — там, куда мы отправляемся, кнопки не нужны


    Логирует всё сам. Поэтому первое, о чём следует вспомнить, подключая его браузерное расширение — блэклист.

    Помимо расширений, предоставляет несколько интеграций и приличное количество плагинов для наиболее распространённых IDE и редакторов.


    Плагины для IDE трекают время, проведённое как в определённой git-ветке, так и в определённом файле. На скрине список доступных — весьма впечатляет. Серенькие — “в планах”, за их скорейшую реализацию можно проголосовать. Некоторые плагины в платной подписке за 9$ в месяц.

    Кроме того, на дашборде можно увидеть, сколько времени в процентном соотношении потрачено на написание кода на разных языках: если вспомнить, что обычный android-проект предполагает использование Kotlin, Java, Groovy и xml — это вполне себе обретает смысл). А если поработать с установленным плагином некоторое время, можно заметить, что он довольно безжалостен: в дашборд попадает время активного чтения и написания кода. И не попадает залипание в монитор со стеклянным взглядом. Что и говорить, на любителя.

    Хмм, исходный код WakaTime открыт: можно попытаться понять, как он работает...
    Это уже интереснее, правда? Вот и я не могла пройти мимо и выкачала себе исходники плагина для IDE от JetBrains.

    Попробуем разобраться. Основная логика находится в WakaTime.java. Там мы можем увидеть, как плагин навешивает листенеры на редактирование и скролл документов, заполняет очередь из Heartbeat (), и через WakaTime CLI отправляет её на сервер. Логика расчёта времени остаётся за кадром, но, кажется, мы и сами в состоянии написать подобное.

    Пишем свой велосипед по аналогии


    Возможно, пушка для стрельбы картошкой и то была бы полезнее, но чего не сделаешь в исследовательских целях) Помедитировав на исходники WakaTime, я решила сделать свой плагин, способный отслеживать активность в IDE, логировать время на его написание, а ещё отсылать всё это в Jira (в нашем флоу ветки называются по именам задач в Jira, поэтому выглядит вполне реалистично).

    А чтобы не засыпать сервер запросами во время работы над кодом, логи мы будем отправлять при закрытии Android Studio, а до этого момента хранить локально.

    1. Какие сущности мы можем встретить в исходном коде плагина (и использовать в своём)?


    Не используем Сomponents (это легаси), ранее представлявшие собой основные структурные единицы плагина. Могут быть уровня приложения, уровня проекта или уровня модуля. В исходниках WakaTime есть ApplicationComponent, но им можно, исходному коду уже лет пять и он должен сохранять обратную совместимость. Компоненты имеют привязку к жизненному циклу уровня, с которым они связаны. Так, например, ApplicationComponent загружается при старте IDE. При использовании будут блокировать перезапуск плагина без перезапуска IDE и вообще ведут себя неприятно. Поэтому вместо них лучше использовать services.

    Используем Services — нынешние основные структурные единицы. Делятся на те же уровни приложения, проекта и модуля, помогут нам инкапсулировать логику на соответствующих уровнях и хранить состояние плагина.

    В отличие от компонентов, Services нужно загружать самостоятельно, используя метод ServiceManager.getService(). Платформа гарантирует, что каждый сервис является синглтоном.

    Добавляем Actions — могут быть шорткатом, дополнительным пунктом меню — словом, отвечают за всё, что как-то затрагивает пользовательские действия в IDE.

    Используем Extensions — любое расширение функциональности, которое будет сложнее Actions: например, подвязаться к жизненному циклу IDE и выполнить что-то во время показа сплэшскрина или перед выходом.

    Всё это должно быть объявлено в файлике /META_INF/plugin.xml.

    2. Plugin.xml и build.gradle



    Окно создания проекта. Мы будем писать наш плагин на Kotlin, а для сборки использовать Gradle

    Это — первое, что мы видим после создания заготовки плагина в IDEA (File -> New -> Project… -> Gradle -> IntelliJ Platform Plugin). Он содержит информацию о зависимостях плагина и его краткое описание. В нём должны быть объявлены компоненты плагина — ранее упомянутые components, services, actions и extensions. Никакой определённой точки входа не будет — ею станет пользовательское действие или событие жизненного цикла IDE, в зависимости от наших потребностей.

    Нам понадобятся вот эти зависимости — ведь мы хотим написать плагин под студию и чтобы он умел работать с Git.

    <depends>Git4Idea</depends>
    <depends>com.intellij.modules.androidstudio</depends>

    Git4Idea является плагином для виртуальной файловой системы (VCS) и предоставляет топики, благодаря которым мы позднее сможем прослушивать события git — такие, как чекаут, например.

    Поскольку мы хотим ещё и того, чтобы плагин умел работать с Jira, подключим через Gradle любезно предоставленную библиотеку с rest-клиентом. Для этого добавим туда maven-репозиторий Atlassian:

    repositories {
       mavenCentral()
       maven {
           url "https://packages.atlassian.com/maven/repository/public"
       }
    }

    И собственно библиотеки:

    implementation "joda-time:joda-time:2.10.4"
    implementation("com.atlassian.jira:jira-rest-java-client-core:4.0.0") {
       exclude group: 'org.slf4j'
       dependencies {
           implementation "com.atlassian.fugue:fugue:2.6.1"
       }
    }

    Здесь мы определяем интересующую нас версию IDE и путь к ней (о, если бы настоящая Android Studio запускалась и работала так же шустро, как её облегчённая версия для отладки плагинов):

    intellij {
       version '2019.1'
       plugins 'git4idea'
       alternativeIdePath 'E:\\Android Studio'
    }

    А здесь, возможно, когда-нибудь напишем о новом, улучшенном функционале. Но не сейчас.

    patchPluginXml {
       changeNotes """
         Add change notes here.<br>
         <em>most HTML tags may be used</em>"""
    }

    3. Создание UI


    Чтобы что-то пушить в Jira, для начала нужно залогиниться. Логично сделать это прямо после открытия проекта. Похоже, нам нужен extension, и мы, не колеблясь, объявим его в plugin.xml:

    <postStartupActivity implementation="Heartbeat"/>

    и в нём покажем диалог.

    UI-компоненты — это главным образом компоненты из Swing с некоторыми расширениями из platform sdk. Всё это с недавних пор обёрнуто в kotlin ui dsl. На момент написания в документации отмечено, что dsl может очень сильно меняться между мажорными версиями, поэтому используем его с лёгким опасением:

    
    override fun createCenterPanel(): JComponent? {
    
       title = "Jira credentials"
       setOKButtonText("Save")
    
       return panel {
           row {
               JLabel("Jira hostname")()
               hostname = JTextField("https://")
               hostname()
           }
           row {
               JLabel("Username:")()
               username = JTextField()
               username()
           }
           row {
               JLabel("Password:")()
               password = JPasswordField()
               password()
           }
       }
    }

    Создали диалог, показали его, получили учётные данные от Jira. Теперь надо их сохранить, и желательно — безопасно, насколько это возможно. На помощь в этом придёт PersistingStateComponent.

    4. Устройство PersistingStateComponent (не очень сложное)


    PersistingStateComponent умеет делать две вещи — saveState и loadState: сериализовывать и сохранять переданный ему объект и извлекать его из хранилища.

    Куда именно он должен сохранять данные, он узнаёт из аннотации State. В документации упомянуты два самых простых варианта — указать свой файл или хранить всё в настройках воркспейса:

    @Storage("yourName.xml")
    @Storage(StoragePathMacros.WORKSPACE_FILE) 
    @State(
       name = "JiraSettings",
       storages = [
           Storage("trackerSettings.xml")
       ])

    Поскольку у нас особый случай, обратимся к документации по хранению чувствительных данных.

    override fun getState(): Credentials? {
       if (credentials == null)  {
           val credentialAttributes = createCredentialAttributes()
           credentials = PasswordSafe.instance.get(credentialAttributes)
       }
       return credentials
    }
    override fun loadState(state: Credentials) {
       credentials = state
       val credentialAttributes = createCredentialAttributes()
       PasswordSafe.instance.set(credentialAttributes, credentials)
    }

    Ещё один PersistingStateComponent будет заниматься хранением времени, залогированного для разных веток.

    С логином в Jira разобрались. С хранением пользовательских данных — тоже. Теперь нужно как-то отследить событие чекаута на ветку, чтобы начать отсчёт времени.

    5. Подписываемся на ивенты


    IntelliJ Platform предоставляет возможность повесить Observer на интересующие нас события, подписавшись на их топики в messageBus нужного уровня. Вот дока, она довольно интересная.

    Топик — это некоторый эндпойнт, представление события. Всё, что нужно сделать — подписаться и реализовать листенер, который должен обрабатывать происходящее.

    Например, мне нужно слушать чекаут в Git…

    subscribeToProjectTopic(project, GitRepository.GIT_REPO_CHANGE) {
        GitRepositoryChangeListener {
            currentBranch = it.currentBranch
        }
    }

    … закрытие приложения (чтобы радостно залогировать время)…

    subscribeToAppTopic(AppLifecycleListener.TOPIC) {
        object: AppLifecycleListener {
            override fun appWillBeClosed(isRestart: Boolean) {
                if (!isRestart) {
                    JiraClient.logTime(entry.key, DateTime.now(), entry.value)
                }
            }
        }
    }

    … и сохранение документов (просто так, чтобы было).

    subscribeToAppTopic(AppTopics.FILE_DOCUMENT_SYNC) {
        CustomSaveListener()
    }

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

    6. EditorEventMulticaster


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

    Теперь у нас есть всё необходимое. Мы можем выделить из текущей ветки имя задачи в Jira (если оно там есть), посчитать время, проведённое в работе над ней, и при выходе из студии незамедлительно дописать его в задачу.

    7. Код и подробности здесь


    Для запуска нужна актуальная Android Studio (3.6.1).
    Для проверки работоспособности — git и ветка с именем, хотя бы отдалённо похожим на таск в Jira.
    Чтобы плагин не просто выводил время в консоль, а трекал его — раскомментить соответствующую строчку в Heartbeat.kt.

    8. Полезные ссылки



    P.S. А что выбрала в итоге?


    — Блин, оно какое-то слишком злое. А если я не пишу код? А если я ковыряю в носу и напряжённо пытаюсь понять, что в нём происходит? В коде, в смысле. Ведь на это и уходит основное время.

    — Тогда хотя бы скролль его.

    — Так и до имитации бурной деятельности недалеко. И потом, я студию могу неделями не выключать и не перезагружать. Может, не надо нам такого счастья?

    — Определённо не надо. Тогда нужен другой повод, а то мне понравилось писать плагины. И с VCS я ещё толком не разбиралась.

    — А ты сама им пользуешься?

    — Не-а. Я как-то руками приучилась логировать, незаметно для себя.
    Skyeng
    Крупнейшая онлайн-школа Европы. Удаленная работа

    Комментарии 8

      +1
      Кстати, в чате разработчиков на удаленке вчера вечером как раз была интересная дискуссия о трекинге времени — безотносительно стека, вышло занятно.
        +1
        Порыться как сделаны плагины логирования — интересно, но пока я дошла до стадии — установила Clockify
        Спасибо за полезную рекомендацию)
          +1
          Wakatime интересен тем что интегрируется практически со всем, но если говорить об Idea, у неё давно есть встроенный функционал управления задачами и трекинга времени, прекрасно дружащий с Jira (и не только). www.jetbrains.com/help/idea/managing-tasks-and-context.html#time_tracking
            0
            Wakatime

            Тоже пользуюсь, нравятся еженедельные отчёты на почту.

            0
            Исходную то задачу вы решили этими утилитами? Т.е. трекать то не сложно, как потом анализировали результаты?
            По этим данным мы пробуем понять, надо ли корректировать эстимейты и нет ли проблем в коммуникации в команде
              0
              Привет, если кратко — то да. Пару лет назад все начиналось с одной из команд, они смогли показать, что так проще выявлять, где буксуют процессы. И постепенно распространили на все: в ноябре проводили опрос тимлидов, логирования не было у трех команд, в феврале этого года уже все 40+ команд так делают.
              0
              Вот читаю а сам думаю:
              — составление таймшита в сам таймшит я уже включая и часто это меня избавляет от его заполнения
              — а не хотят ли господа трекатели, чтобы я трекал и выставлял счет о размышлении над их проблемой ночью, перед сном, за чашечкой кофе, на прогулке? Оно ведь так и есть и от итоговой цифры никто не обрадуется.
                0
                Так

                Тимлид попросил записывать в Jira, сколько времени ушло на кодинг, а сколько на разговоры и ревью. Хотя бы раз в две недели. «По этим данным мы пробуем понять, надо ли корректировать эстимейты и нет ли проблем в коммуникации в команде», — говорили они. А вот кто такой «бабайка»,

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

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое