├── README.md
├── app.controller.filter.js
├── app.controller.splash.js
├── app.controller.table.js
├── app.controller.toolbar.js
├── app.factory.gitlab.js
├── app.js
├── app.service.auth.js
├── callback.html
├── custom.js
├── favico.ico
├── gr-loaders.gif
├── img
├── logo.png
└── logo_dark.png
├── index.html
└── style.css
/README.md:
--------------------------------------------------------------------------------
1 | # GitLab Reports
2 | GitLab Reports is a small utility web app built to help developers (using GitLab for their projects) to generate time tracking reports for their GitLab projects. App's purpose is to provide a clean, quick and to-the-point interface to track progress of the tasks.
3 |
4 |
5 |
6 | ## Features
7 | + After being authenticated by GitLab, developers can select desired project and then view/generate reports on the web based on select Milestone.
8 | + Reports can be created based on select labels (like enhancement, bug, task etc.) and issue states (opened or closed).
9 | + Developers can customize reports data by selecting what columns to show or hide.
10 | + GitLab Reports supports calculating Total Estimate Time and Total Time Spent of all issues including in the project report. This helps developers analyzing that how much total time they have spent on specific project milestones which helps tracking progress.
11 |
12 | ### Save to PDF
13 | In next releases, we will add support of exporting report to different formats specially PDF along with other enhancements and bug fixes. However, users can still create reports in PDF format by clicking Print button in app's header-toolbar and then selecting **Save as PDF** in destination as shown in below screen shot:
14 |
15 |
16 |
17 | ## Resources
18 | + **GitLab Reports App:** [http://gitlabreports.cosango.com/](http://gitlabreports.cosango.com/)
19 | + **Cosango Apps:** [http://cosango.com/apps/](http://cosango.com/apps/)
20 | + **Cosango:** [http://cosango.com/](http://cosango.com)
--------------------------------------------------------------------------------
/app.controller.filter.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | function main($rootScope, $scope, $mdSidenav, gitlab, auth) {
5 |
6 | $scope.$watch('project', function () {
7 | $scope.labels = null;
8 | $scope.milestone = null;
9 | $scope.state = null;
10 | localStorage.removeItem('labels');
11 | localStorage.removeItem('milestone');
12 | localStorage.removeItem('state');
13 |
14 | if ($scope.project) {
15 | $scope.loadProjectLabels();
16 | $scope.loadProjectMilestones();
17 | }
18 | });
19 |
20 | $rootScope.$watch('show_comulative_time_estimate', function () {
21 | console.log($rootScope.show_comulative_time_estimate);
22 | });
23 |
24 | $scope.$watch('labels', function () {
25 | if ($scope.labels) {
26 | }
27 | });
28 |
29 | $scope.$watch('state', function () {
30 | if ($scope.state) {
31 | }
32 | });
33 |
34 | $scope.$watch('milestone', function () {
35 | if ($scope.milestone) {
36 | }
37 | });
38 |
39 | $scope.loadProjects = function () {
40 | $scope.projects = gitlab.projects.query({
41 | access_token: auth.getAccessToken(),
42 | membership: true
43 | });
44 | };
45 |
46 | $scope.loadProjectLabels = function () {
47 | if (!$scope.project) {
48 | return;
49 | }
50 |
51 | $scope.project_labels = gitlab.projects_labels.query({
52 | access_token: auth.getAccessToken(),
53 | id: $scope.project.id
54 | });
55 | };
56 |
57 | $scope.loadProjectMilestones = function () {
58 | if (!$scope.project) {
59 | return;
60 | }
61 |
62 | $scope.project_milestones = gitlab.projects_milestones.query({
63 | access_token: auth.getAccessToken(),
64 | id: $scope.project.id
65 | });
66 | };
67 |
68 | $scope.applyFilter = function () {
69 | if (!angular.isObject($scope.project)) {
70 | return;
71 | }
72 |
73 | localStorage.setItem('project_id', $scope.project.id);
74 |
75 | if ($scope.state) {
76 | localStorage.setItem('state', $scope.state);
77 | }
78 | if (angular.isObject($scope.milestone)) {
79 | localStorage.setItem('milestone', $scope.milestone.title);
80 | }
81 | if (angular.isObject($scope.labels)) {
82 | var l = [];
83 | angular.forEach($scope.labels, function (item) {
84 | l.push(item.name);
85 | });
86 | localStorage.setItem('labels', l.join(','));
87 | }
88 |
89 | $rootScope.$broadcast('local-storage-updated');
90 | };
91 |
92 | $rootScope.show_comulative_time_estimate = true;
93 | $rootScope.show_comulative_time_spent = true;
94 | $rootScope.show_show_logo_print = true;
95 | $rootScope.table_columns = [
96 | 'srno',
97 | 'iid',
98 | 'title',
99 | 'created_at',
100 | 'author',
101 | 'assignee',
102 | 'time_estimate',
103 | 'total_time_spent'
104 | ];
105 | $scope.loadProjects();
106 | }
107 |
108 | angular.module('GitLabReportApp').controller('FilterController', main);
109 |
110 | })();
111 |
112 |
--------------------------------------------------------------------------------
/app.controller.splash.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | function main($scope, auth) {
5 |
6 | $scope.$watch('authRequired', function () {
7 | window.addEventListener('load', function () {
8 | setTimeout(function () {
9 | if ($scope.authRequired) {
10 | auth.redirectToOauth();
11 | } else {
12 | $("#loader").fadeOut("slow");
13 | }
14 | }, 2000);
15 | });
16 | });
17 |
18 | $scope.authRequired = localStorage.getItem('access_token') === null;
19 |
20 |
21 | }
22 |
23 | angular.module('GitLabReportApp').controller('SplashController', main);
24 |
25 | })();
26 |
27 |
--------------------------------------------------------------------------------
/app.controller.table.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | function main($rootScope, $scope, gitlab, auth) {
5 |
6 | $rootScope.$on('local-storage-updated', function () {
7 | $scope.comulative_time_spent = 0;
8 | $scope.comulative_time_estimate = 0;
9 | $scope.loadIssues();
10 | });
11 |
12 | $scope.loadIssues = function () {
13 | var params = {};
14 | if (localStorage.getItem('access_token')) {
15 | params.access_token = localStorage.getItem('access_token');
16 | } else {
17 | return;
18 | }
19 | if (localStorage.getItem('project_id')) {
20 | params.id = localStorage.getItem('project_id');
21 | } else {
22 | return;
23 | }
24 | if (localStorage.getItem('state')) {
25 | params.state = localStorage.getItem('state');
26 | }
27 | if (localStorage.getItem('milestone')) {
28 | params.milestone = localStorage.getItem('milestone');
29 | }
30 | if (localStorage.getItem('labels')) {
31 | params.labels = localStorage.getItem('labels');
32 | }
33 | $scope.issues = gitlab.projects_issues.query(
34 | params,
35 | function () {
36 | },
37 | function () {
38 | auth.redirectToOauth();
39 | }
40 | );
41 | };
42 |
43 | $scope.getIssuesTimeStats = function (issue_project_id, issue_iid) {
44 | var stats = gitlab.issues_time_stats.get(
45 | {
46 | access_token: localStorage.getItem('access_token'),
47 | id: issue_project_id,
48 | issue_iid: issue_iid
49 | },
50 | function () {
51 | $scope.comulative_time_estimate += stats.time_estimate;
52 | $scope.comulative_time_spent += stats.total_time_spent;
53 | console.log($scope.comulative_time_estimate, $scope.comulative_time_spent);
54 | }
55 | );
56 |
57 | return stats;
58 | };
59 |
60 | }
61 |
62 |
63 |
64 | angular.module('GitLabReportApp').controller('TableController', main);
65 |
66 | })();
67 |
68 |
--------------------------------------------------------------------------------
/app.controller.toolbar.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | function main($rootScope, $scope, $mdSidenav) {
5 | $scope.logout = function () {
6 | localStorage.removeItem('access_token');
7 | location.href = 'http://www.cosango.com/';
8 | };
9 |
10 | $scope.showMe = true;
11 |
12 | $scope.toggleRightSidenav = function () {
13 | console.log('$rootScope.right_sidenav_locked_open', $rootScope.right_sidenav_locked_open);
14 | $rootScope.right_sidenav_locked_open = !$rootScope.right_sidenav_locked_open;
15 | $scope.showMe = !$scope.showMe;
16 | };
17 |
18 | $rootScope.right_sidenav_locked_open = true
19 | }
20 |
21 | angular.module('GitLabReportApp').controller('ToolbarController', main);
22 |
23 | })();
24 |
25 |
--------------------------------------------------------------------------------
/app.factory.gitlab.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | function main($resource) {
5 |
6 | return {
7 | issues: $resource(
8 | 'https://gitlab.com/api/v4/issues',
9 | {},
10 | {
11 | query: {
12 | method: 'GET',
13 | isArray: true
14 | }
15 | }
16 | ),
17 | issues_time_stats: $resource(
18 | 'https://gitlab.com/api/v4/projects/:id/issues/:issue_iid/time_stats',
19 | {},
20 | {
21 | get: {
22 | method: 'GET'
23 | }
24 | }
25 | ),
26 | projects: $resource(
27 | 'https://gitlab.com/api/v4/projects',
28 | {},
29 | {
30 | query: {
31 | method: 'GET',
32 | isArray: true
33 | }
34 | }
35 | ),
36 | projects_issues: $resource(
37 | 'https://gitlab.com/api/v4/projects/:id/issues',
38 | {},
39 | {
40 | query: {
41 | method: 'GET',
42 | isArray: true
43 | }
44 | }
45 | ),
46 | projects_labels: $resource(
47 | 'https://gitlab.com/api/v4/projects/:id/labels',
48 | {},
49 | {
50 | query: {
51 | method: 'GET',
52 | isArray: true
53 | }
54 | }
55 | ),
56 | projects_milestones: $resource(
57 | 'https://gitlab.com/api/v4/projects/:id/milestones',
58 | {},
59 | {
60 | query: {
61 | method: 'GET',
62 | isArray: true
63 | }
64 | }
65 | )
66 | };
67 | }
68 |
69 | angular.module('GitLabReportApp').factory('gitlab', main);
70 |
71 | })();
72 |
73 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular.module('GitLabReportApp', ['ngMaterial', 'ngResource']);
5 |
6 |
7 | })();
8 |
9 |
--------------------------------------------------------------------------------
/app.service.auth.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | function main() {
5 |
6 | return {
7 | getAccessToken: function () {
8 | return localStorage.getItem('access_token');
9 | },
10 | redirectToOauth: function () {
11 | var client_id = 'f2a8860f24b57b1359af88f83b925ceef95f47a139b42e3abd08f87fe8b97830';
12 | var redirect_uri = encodeURI('http://gitlabreports.cosango.com/callback.html');
13 | var authorize_url = "https://gitlab.com/oauth/authorize?&client_id=" + client_id + "&redirect_uri=" + redirect_uri + "&scope=api&response_type=token&state=1";
14 | location.href = authorize_url;
15 | }
16 |
17 | };
18 | }
19 |
20 | angular.module('GitLabReportApp').service('auth', main);
21 |
22 | })();
23 |
24 |
--------------------------------------------------------------------------------
/callback.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
34 | GITLABREPORTS 35 | You will be redirected to GitLab Sign-in page. 36 |
37 |No | 97 |ID | 98 |99 | short_text 100 | Title 101 | | 102 |103 | date_range 104 | Date 105 | | 106 |107 | Author 108 | | 109 |110 | Assignee 111 | | 112 |113 | access_time 114 | Time Estimate 115 | | 116 |117 | slow_motion_video 118 | Time Spent 119 | | 120 |
---|---|---|---|---|---|---|---|
125 | {{ $index + 1 }} 126 | | 127 |128 | {{ issue.iid }} 129 | | 130 |131 | {{ issue.title }} 132 | | 133 |135 | {{ issue.created_at | date:'yyyy-MM-dd' }} 136 | | 137 |138 | {{ issue.author.name }} 139 | | 140 |141 | {{ issue.assignee.name }} 142 | | 143 |145 | Unspecified 146 | {{ stats.human_time_estimate }} 147 | | 148 |150 | Unspecified 151 | {{ stats.human_total_time_spent }} 152 | | 153 |