Статьи

Angular Commit Bubbles

Я одержим псевдо-декларативной стороной Angular. Создание HTML Turing-complete было гениальным ходом. Чтобы проверить теорию и найти что-то действительно полезное, я подумал, что сдую пыль с чего-то другого, что Мишко создал в прошлом …

Совершить пузыри!

Мишко Хевери написал приложение ActionScript, которое показало энтузиазм в тестировании разработчиков в команде. Ранее он работал в Adobe и был достаточно опытным в экосистеме Flash. Я уверен, что он забыл многое из этого, учитывая, насколько важным и трудоемким стал Angular. Об этом рассказывал бывший ThoughtWorker Джон Уолтер . Я думаю, что Джон даже помог Мишко развить его пару лет назад. У нас есть только скриншоты оригинального приложения:

Во всяком случае, мы воссоздали его в Angular как командную работу после работы. Честно говоря, это также инструмент обучения Angular для новичков в нем, и я переключаюсь между «владельцем продукта» и «угловым кодером» по мере необходимости. Это также послужило руководством для SVG.

Если у вас IE 10 или выше, вы можете поиграть с ним здесь (и на мобильном телефоне): http://paul-hammant.github.io/angular-commit-bubbles/commits.html

Флажки и кнопки работают. Они удаляют (или добавляют обратно) коммиттеры, так что вы можете найти лучшего исполнителя (или худшего, или обоих). Вы также можете нажать на пузырьки для наложения, показывая больше информации о коммите. Это только jpeg выше, так что не расстраивайтесь, если вы нажали на него. Настоящая сделка здесь

В реализации у нас есть преднамеренная нефункциональная цель: как будет выглядеть решение с наименьшим количеством возможных строк JavaScript. Источник здесь , но для вашего удобства здесь тоже:

<!DOCTYPE html>
<html>
<head>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
    <meta content="utf-8" http-equiv="encoding">
    <title>Commit Bubbles</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js"></script>
    <script src="http://underscorejs.org/underscore-min.js"></script>
    <script src="http://vitalets.github.io/checklist-model/checklist-model.js"></script>
    <script src="http://momentjs.com/downloads/moment.min.js"></script>
    <script>
        var app = angular.module("app", ["checklist-model"]);
        app.controller('CommitBubbles', function ($scope) {
            $scope.c = {
                chartHeight: 680,
                topMargin: 100,
                colors: ["#FFA07A","#20B2AA","#778899","#B0C4DE","#F5F5DC","#00FF00","#000000","#32CD32","#FAF0E6","#0000FF","#FF00FF","#8A2BE2","#800000","#A52A2A","#66CDAA","#DEB887","#0000CD","#5F9EA0","#7FFF00","#9370DB","#D2691E","#FF7F50","#7B68EE","#6495ED","#48D1CC","#DC143C","#C71585","#00FFFF","#191970","#00008B","#F5FFFA","#008B8B","#FFE4E1","#B8860B","#A9A9A9","#006400","#000080","#BDB76B","#8B008B","#808000","#6B8E23","#FF8C00","#FFA500","#9932CC","#FF4500","#8B0000","#DA70D6","#E9967A","#EEE8AA","#8FBC8F","#98FB98","#483D8B","#AFEEEE","#2F4F4F","#DB7093","#00CED1","#9400D3","#FFDAB9","#FF1493","#CD853F","#00BFFF","#FFC0CB","#696969","#DDA0DD","#1E90FF","#B0E0E6","#B22222","#800080","#FFFAF0","#FF0000","#228B22","#BC8F8F","#FF00FF","#4169E1","#DCDCDC","#FA8072","#FFD700","#FAA460","#DAA520","#2E8B57","#808080","#008000","#A0522D","#ADFF2F","#C0C0C0","#87CEEB","#FF69B4","#6A5ACD","#CD5C5C","#708090","#4B0082","#00FF7F","#F0E68C","#4682B4","#E6E6FA","#D2B48C","#008080","#7CFC00","#D8BFD8","#FFFACD","#FF6347","#ADD8E6","#40E0D0","#F08080","#EE82EE","#F5DEB3","#FAFAD2","#90EE90","#D3D3D3","#FFFF00","#FFB6C1"]
            };
            $scope.commits = [
                {
                    ts:1288202623006,
                    hash:"1234567890",
                    testPercentage:100,
                    size:55,
                    author:"John Doe"
                },
                {
                    ts:1288121623006,
                    hash:"13432435645",
                    testPercentage:30,
                    size:81,
                    author:"John Doe"
                },
                {
                    ts:1288128023006,
                    hash:"12232435645",
                    testPercentage:50,
                    size:50,
                    author:"Hector"
                },
                {
                    ts:1288202823006,
                    hash:"12332343455",
                    testPercentage:55,
                    size:50,
                    author:"Hector"
                },
                {
                    ts:1288323623006,
                    hash:"2342342344",
                    testPercentage:15,
                    size:22,
                    author:"Mildred"
                }
            ];
            $scope.authors = _.uniq(_.pluck($scope.commits, 'author'));
            $scope.selected = { authors: $scope.authors.slice(0) };
            $scope.from = _.min($scope.commits, "ts").ts;
            $scope.to = _.max($scope.commits, "ts").ts;
            $scope.timeSpan = moment.duration(moment($scope.from).diff(moment($scope.to))).humanize();
            $scope.checkAllAuthors = function() {
              $scope.selected.authors = angular.copy($scope.authors);
            };
        });
    </script>
</head>
<body ng-app="app" ng-controller="CommitBubbles" bgcolor="#FFFFFF">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" width="1220"
     height="{{c.chartHeight + c.topMargin + 5}}"
     xmlns:dc="http://purl.org/dc/elements/1.1/">
    <rect fill="#FEFCFF" x="100" y="{{c.topMargin}}" width="1100" height="{{c.chartHeight}}" ng-click="selected.commit = false"/>
    <g font-family="Verdana" font-size="16">
        <text x="0" y="{{c.topMargin + 8}}">All Test</text>
        <text x="0" y="{{c.chartHeight/2 + 5 + c.topMargin}}">Equal</text>
        <text x="0" y="{{c.chartHeight + c.topMargin}}">All Prod</text>
        <text x="1140" y="{{c.chartHeight/2 + 17 + c.topMargin}}">Time &#10148;</text>
    </g>
    <g style="stroke:rgb(0,0,0);stroke-width:2">
        <line x1="100" y1="{{c.chartHeight/2 + c.topMargin}}" x2="1200" y2="{{c.chartHeight/2 + c.topMargin}}" />
        <line x1="100" y1="{{c.topMargin +2}}" x2="100" y2="{{c.chartHeight + c.topMargin+2}}"/>
    </g>
    <g font-family="Verdana" font-size="8" ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" style="stroke:rgb(0,0,0);stroke-width:0.5">
        <line ng-show="i != 5" x1="100" stroke-dasharray="10,35"
              y1="{{c.chartHeight + 2 - (c.chartHeight/10 * i) + c.topMargin}}" x2="1200"
              y2="{{c.chartHeight - (c.chartHeight/10 * i) + c.topMargin}}" />
        <text fill="white" x="70" y="{{c.chartHeight + 3  - (c.chartHeight/10 * i) + c.topMargin}}">{{100 - (10*i)}}:{{(10*i)}}</text>
    </g>
    <g stroke="black">
        <circle ng-repeat="commit in commits" ng-show="selected.authors.indexOf(commit.author) > -1"
                cx="{{100 + (commit.ts - from) * 1100 / (to - from) }}"
                cy="{{c.chartHeight - (commit.testPercentage * c.chartHeight/100) + c.topMargin}}"
                r="{{commit.size * (c.chartHeight/600)}}"
                stroke-opacity="{{(selected.commit === commit) ? 1 : 0}}"
                fill="{{c.colors}}" fill-opacity="0.5"
                ng-click="selected.commit = commit"/>
    </g>
    <foreignObject class="node" x="0" y="0" width="1200" height="{{c.topMargin}}">
        <body xmlns="http://www.w3.org/1999/xhtml">
        <h2>{{timeSpan}} shown between {{from | date:'MM/dd/yy HH:mm'}} and {{to | date:'MM/dd/yy HH:mm'}}</h2>
        <div>Show authors:
            <label ng-repeat="author in authors" style="{{'color: ' + c.colors[$index] }}">
                <input type="checkbox" checklist-model="selected.authors" checklist-value="author"> {{author}}
            </label>
            <button ng-click="checkAllAuthors()" style="margin: 0 0 5px 5px">All authors</button>
            <button ng-click="selected.authors.splice(0,selected.authors.length)" style="margin: 0 0 5px 5px">None</button>
        </div>
        </body>
    </foreignObject>
    <g font-family="Verdana" font-size="16" ng-show="selected.commit" ng-click="selected.commit = false">
        <rect style="stroke:rgb(0,0,0);stroke-width:2" x="300" y="300" height="300" width="400" fill="white" opacity="0.7"/>
        <text x="320" y="330">Selected Commit</text>
        <text x="320" y="380">Hash: {{selected.commit.hash}}</text>
        <text x="320" y="430">Author: {{selected.commit.author}}</text>
        <text x="320" y="480">Date/Time: {{selected.commit.ts | date:'yyyy-MM-dd HH:mm:ss Z'}}</text>
        <text x="320" y="530">Size: {{selected.commit.size}}</text>
        <text x="320" y="580">Test Percentage: {{selected.commit.testPercentage}}</text>
    </g>
</svg>
</body>
</html>

Если мы проигнорируем массив не конфликтующих цветов, некоторые константы и симуляцию четырех коммитов, которые встроены, а не извлекаются через $ resource, то есть только десять строк JavaScript. Подчеркивание помогло нам немного сократить количество строк. Недостатком является то, что html довольно тяжелый угловой атрибут.

Следующий?

Colleagues [a href=»http://www.grahambrooks.com»]Graham Brooks and Premanand Chandrasekaran are working to use it in a real (and larger system), and populate the commit info (at least for Subversion).

A D3 Alternative

Colleague Pawel Badenski forked my repo, and quickly made a D3 version of the same thing. I don’t want to give him too much publicity, so I’ll leave it at that

20% less lines of code, grumble grumble……