Skip to content

Commit a2b13ed

Browse files
authored
Merge pull request #32 from DevOps-zhuang/feature/SeatAnalysis
add copilot seat analysis supports
2 parents 18a0c42 + de79474 commit a2b13ed

File tree

8 files changed

+513
-6
lines changed

8 files changed

+513
-6
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,20 @@ The language breakdown analysis tab also displays a table showing the Accepted P
7070

7171
4. **Total Active Copilot Chat Users:** a bar chart that illustrates the total number of users who have actively interacted with Copilot over the past 28 days.
7272

73+
## Seat Analysis
74+
75+
![image](https://github.com/DevOps-zhuang/copilot-metrics-viewer/assets/54096296/d1fa9d1d-4fab-4e87-84ba-7be189dd4dd0)
76+
77+
1. **Total Assigned:** This metric represents the total number of Copilot seats assigned within current organization.
78+
79+
2. **Assigned But Never Used:** This metric shows seats that were assigned but never within the current organization. The assigned timestamp is also displayed in the below chart.
80+
81+
3. **No Activity in the Last 7 days:** never used seats or seats used, but with no activity in the past 7 days.
82+
83+
4. **No Activity in the last 7 days (including never used seats):**a table to display seats that have had no activity in the past 7 days, ordered by the date of last activity. Seats that were used earlier are displayed at the top.
84+
85+
86+
7387
## Setup instructions
7488

7589
In the `.env` file, you can configure several environment variables that control the behavior of the application.

src/api/ExtractSeats.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// TypeScript
2+
import axios from "axios";
3+
import { Seat } from "../model/Seat";
4+
5+
import organizationMockedResponse_seats from '../assets/organization_response_sample_seats.json';
6+
import enterpriseMockedResponse_seats from '../assets/enterprise_response_sample_seats.json';
7+
8+
export const getSeatsApi = async (): Promise<Seat[]> => {
9+
const perPage = 50;
10+
let page = 1;
11+
let seatsData: Seat[] = [];
12+
13+
let response;
14+
if (process.env.VUE_APP_SCOPE !== "organization") {
15+
// when the scope is not organization, return seatsData,by default it will return empty array
16+
return seatsData;
17+
}
18+
else{
19+
if (process.env.VUE_APP_MOCKED_DATA === "true") {
20+
response = organizationMockedResponse_seats;
21+
seatsData = seatsData.concat(response.seats.map((item: any) => new Seat(item)));
22+
}
23+
else if (process.env.VUE_APP_MOCKED_DATA === "false") {
24+
// Fetch the first page to get the total number of seats
25+
response = await axios.get(`https://api.github.com/orgs/${process.env.VUE_APP_GITHUB_ORG}/copilot/billing/seats`, {
26+
headers: {
27+
Accept: "application/vnd.github+json",
28+
Authorization: `Bearer ${process.env.VUE_APP_GITHUB_TOKEN}`,
29+
"X-GitHub-Api-Version": "2022-11-28",
30+
},
31+
params: {
32+
per_page: perPage,
33+
page: page
34+
}
35+
});
36+
37+
seatsData = seatsData.concat(response.data.seats.map((item: any) => new Seat(item)));
38+
// Calculate the total pages
39+
const totalSeats = response.data.total_seats;
40+
const totalPages = Math.ceil(totalSeats / perPage);
41+
42+
// Fetch the remaining pages
43+
for (page = 2; page <= totalPages; page++) {
44+
response = await axios.get(`https://api.github.com/orgs/${process.env.VUE_APP_GITHUB_ORG}/copilot/billing/seats`, {
45+
headers: {
46+
Accept: "application/vnd.github+json",
47+
Authorization: `Bearer ${process.env.VUE_APP_GITHUB_TOKEN}`,
48+
"X-GitHub-Api-Version": "2022-11-28",
49+
},
50+
params: {
51+
per_page: perPage,
52+
page: page
53+
}
54+
});
55+
56+
seatsData = seatsData.concat(response.data.seats.map((item: any) => new Seat(item)));
57+
} //end of else if (process.env.VUE_APP_MOCKED_DATA === "false")
58+
} //end of else if (process.env.VUE_APP_SCOPE !== "organization")
59+
return seatsData;
60+
}
61+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
{
2+
"total_seats": 2,
3+
"seats": [
4+
{
5+
"created_at": "2021-08-03T18:00:00-06:00",
6+
"updated_at": "2021-09-23T15:00:00-06:00",
7+
"pending_cancellation_date": null,
8+
"last_activity_at": "2021-10-14T00:53:32-06:00",
9+
"last_activity_editor": "vscode/1.77.3/copilot/1.86.82",
10+
"assignee": {
11+
"login": "octocat",
12+
"id": 1,
13+
"node_id": "MDQ6VXNlcjE=",
14+
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
15+
"gravatar_id": "",
16+
"url": "https://api.github.com/users/octocat",
17+
"html_url": "https://github.com/octocat",
18+
"followers_url": "https://api.github.com/users/octocat/followers",
19+
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
20+
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
21+
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
22+
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
23+
"organizations_url": "https://api.github.com/users/octocat/orgs",
24+
"repos_url": "https://api.github.com/users/octocat/repos",
25+
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
26+
"received_events_url": "https://api.github.com/users/octocat/received_events",
27+
"type": "User",
28+
"site_admin": false
29+
},
30+
"assigning_team": {
31+
"id": 1,
32+
"node_id": "MDQ6VGVhbTE=",
33+
"url": "https://api.github.com/teams/1",
34+
"html_url": "https://github.com/orgs/github/teams/justice-league",
35+
"name": "Justice League",
36+
"slug": "justice-league",
37+
"description": "A great team.",
38+
"privacy": "closed",
39+
"notification_setting": "notifications_enabled",
40+
"permission": "admin",
41+
"members_url": "https://api.github.com/teams/1/members{/member}",
42+
"repositories_url": "https://api.github.com/teams/1/repos",
43+
"parent": null
44+
}
45+
},
46+
{
47+
"created_at": "2021-09-23T18:00:00-06:00",
48+
"updated_at": "2021-09-23T15:00:00-06:00",
49+
"pending_cancellation_date": "2021-11-01",
50+
"last_activity_at": "2021-10-13T00:53:32-06:00",
51+
"last_activity_editor": "vscode/1.77.3/copilot/1.86.82",
52+
"assignee": {
53+
"login": "octokitten",
54+
"id": 1,
55+
"node_id": "MDQ76VNlcjE=",
56+
"avatar_url": "https://github.com/images/error/octokitten_happy.gif",
57+
"gravatar_id": "",
58+
"url": "https://api.github.com/users/octokitten",
59+
"html_url": "https://github.com/octokitten",
60+
"followers_url": "https://api.github.com/users/octokitten/followers",
61+
"following_url": "https://api.github.com/users/octokitten/following{/other_user}",
62+
"gists_url": "https://api.github.com/users/octokitten/gists{/gist_id}",
63+
"starred_url": "https://api.github.com/users/octokitten/starred{/owner}{/repo}",
64+
"subscriptions_url": "https://api.github.com/users/octokitten/subscriptions",
65+
"organizations_url": "https://api.github.com/users/octokitten/orgs",
66+
"repos_url": "https://api.github.com/users/octokitten/repos",
67+
"events_url": "https://api.github.com/users/octokitten/events{/privacy}",
68+
"received_events_url": "https://api.github.com/users/octokitten/received_events",
69+
"type": "User",
70+
"site_admin": false
71+
}
72+
}
73+
]
74+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
{
2+
"total_seats": 2,
3+
"seats": [
4+
{
5+
"created_at": "2021-08-03T18:00:00-06:00",
6+
"updated_at": "2021-09-23T15:00:00-06:00",
7+
"pending_cancellation_date": null,
8+
"last_activity_at": "2021-10-14T00:53:32-06:00",
9+
"last_activity_editor": "vscode/1.77.3/copilot/1.86.82",
10+
"assignee": {
11+
"login": "octocat",
12+
"id": 1,
13+
"node_id": "MDQ6VXNlcjE=",
14+
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
15+
"gravatar_id": "",
16+
"url": "https://api.github.com/users/octocat",
17+
"html_url": "https://github.com/octocat",
18+
"followers_url": "https://api.github.com/users/octocat/followers",
19+
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
20+
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
21+
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
22+
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
23+
"organizations_url": "https://api.github.com/users/octocat/orgs",
24+
"repos_url": "https://api.github.com/users/octocat/repos",
25+
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
26+
"received_events_url": "https://api.github.com/users/octocat/received_events",
27+
"type": "User",
28+
"site_admin": false
29+
},
30+
"assigning_team": {
31+
"id": 1,
32+
"node_id": "MDQ6VGVhbTE=",
33+
"url": "https://api.github.com/teams/1",
34+
"html_url": "https://github.com/orgs/github/teams/justice-league",
35+
"name": "Justice League",
36+
"slug": "justice-league",
37+
"description": "A great team.",
38+
"privacy": "closed",
39+
"notification_setting": "notifications_enabled",
40+
"permission": "admin",
41+
"members_url": "https://api.github.com/teams/1/members{/member}",
42+
"repositories_url": "https://api.github.com/teams/1/repos",
43+
"parent": null
44+
}
45+
},
46+
{
47+
"created_at": "2021-09-23T18:00:00-06:00",
48+
"updated_at": "2021-09-23T15:00:00-06:00",
49+
"pending_cancellation_date": "2021-11-01",
50+
"last_activity_at": "2021-10-13T00:53:32-06:00",
51+
"last_activity_editor": "vscode/1.77.3/copilot/1.86.82",
52+
"assignee": {
53+
"login": "octokitten",
54+
"id": 1,
55+
"node_id": "MDQ76VNlcjE=",
56+
"avatar_url": "https://github.com/images/error/octokitten_happy.gif",
57+
"gravatar_id": "",
58+
"url": "https://api.github.com/users/octokitten",
59+
"html_url": "https://github.com/octokitten",
60+
"followers_url": "https://api.github.com/users/octokitten/followers",
61+
"following_url": "https://api.github.com/users/octokitten/following{/other_user}",
62+
"gists_url": "https://api.github.com/users/octokitten/gists{/gist_id}",
63+
"starred_url": "https://api.github.com/users/octokitten/starred{/owner}{/repo}",
64+
"subscriptions_url": "https://api.github.com/users/octokitten/subscriptions",
65+
"organizations_url": "https://api.github.com/users/octokitten/orgs",
66+
"repos_url": "https://api.github.com/users/octokitten/repos",
67+
"events_url": "https://api.github.com/users/octokitten/events{/privacy}",
68+
"received_events_url": "https://api.github.com/users/octokitten/received_events",
69+
"type": "User",
70+
"site_admin": false
71+
}
72+
}
73+
]
74+
}

src/components/ApiResponse.vue

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@
1111
<div v-if="showCopyMessage" :class="{'copy-message': true, 'error': isError}">{{ message }}</div>
1212
</transition>
1313
</div>
14+
15+
<br><br>
16+
<div v-if="vueAppScope === 'organization'">
17+
<v-card max-height="575px" class="overflow-y-auto">
18+
<pre ref="jsonText">{{ JSON.stringify(seats, null, 2) }}</pre>
19+
</v-card>
20+
<br>
21+
<div class="copy-container">
22+
<v-btn @click="showSeatCount">Show Assigned Seats count</v-btn>
23+
<transition name="fade">
24+
<div v-if="showSeatMessage" :class="{'copy-message': true, 'error': isError}">{{ message }}</div>
25+
</transition>
26+
</div>
27+
</div>
1428
</v-container>
1529
</template>
1630

@@ -23,11 +37,17 @@ export default defineComponent({
2337
metrics: {
2438
type: Object,
2539
required: true
40+
},
41+
seats: {
42+
type: Array,
43+
required: true
2644
}
2745
},
2846
data() {
2947
return {
48+
vueAppScope: process.env.VUE_APP_SCOPE,
3049
showCopyMessage: false,
50+
showSeatMessage: false,
3151
isError: false,
3252
message : ''
3353
@@ -51,7 +71,19 @@ export default defineComponent({
5171
setTimeout(() => {
5272
this.showCopyMessage = false;
5373
}, 3000);
74+
},
75+
76+
showSeatCount() {
77+
const seatCount = this.seats.length;
78+
//console.log('Seat count:', seatCount);
79+
this.message = `Seat count: ${seatCount}`;
80+
81+
this.showSeatMessage = true;
82+
setTimeout(() => {
83+
this.showSeatMessage = false;
84+
}, 3000);
5485
}
86+
5587
}
5688
5789
});

0 commit comments

Comments
 (0)