はじめに
Copilot のメトリックの REST API エンドポイントを使うと、ユーザーが GitHub Copilot を採用している傾向を確認できます。 GitHub Copilot のロールアウト中にこれらの傾向を確認すると、ユーザーが自分に割り当てられたライセンスを使っていることをチェックし、ユーザーが使っている機能を表示し、会社の有効化計画が開発者に与える影響を把握するために役立ちます。
この API には次の要素が含まれています。
- 過去 28 日間のデータ
- アクティブ ユーザーとエンゲージメントの高いユーザーの数
- 言語と IDE 別の内訳
- Enterprise、organization、またはチームのメトリックを表示するオプション
現在 GitHub Copilot の使用状況メトリックの REST API エンドポイント を使っている場合は、できるだけ早く Copilot のメトリックの REST API エンドポイント に移行することをお勧めします。
このガイドでは、API のクエリを実行し、データを保存し、週ごとのユーザー数の変化の傾向を分析する方法について説明します。 このガイドの例では organization のエンドポイントを使っていますが、ニーズに合わせて例を調整できます。
エンドポイントの可用性について
エンドポイントは、Enterprise、organization、organization チーム、または Enterprise チームのデータを取得するために使用できます。
- 通常の organization または Enterprise の一部として GitHub Copilot Business または GitHub Copilot Enterprise サブスクリプションをお持ちの場合は、Enterprise、organization、または organization チームのエンドポイントを使用できます。 プレビューに登録していない場合、Enterprise チームにはアクセスできません。
- GitHub Copilot Business 専用の Enterprise (organization を作成できない Enterprise アカウント) を使う場合は、Enterprise または Enterprise チームのエンドポイントを使用できます。
前提条件
- Enterprise または organization に対して、[Copilot metrics API access] ポリシーを有効にする必要があります。 「organization 内での Copilot のポリシーの管理」または「企業内の Copilot のポリシーと機能を管理する」を参照してください。
- クエリ対象の organization、Enterprise、またはチームには、十分な数のアクティブな Copilot ユーザーがいる必要があります。 その日に有効な Copilot ライセンスを持つメンバーが 5 人以上いる場合にのみ、API からその日の結果が返されます。
- この例では、データのクエリと分析を行うための JavaScript スクリプトを作成します。 このスクリプトをローカルで実行するには、Node.js をインストールしてから、
npm install -g octokit
を使って Octokit.js SDK をインストールする必要があります。
1.personal access token
を作成する
この例では、organization のメトリックを取得するために、manage_billing:copilot
スコープを指定して personal access token (classic) を作成します。 「個人用アクセス トークンを管理する」を参照してください。
別のエンドポイントを使っている場合は、別のスコープが必要になる場合があります。 「Copilot のメトリックの REST API エンドポイント」を参照してください。
2. API に接続する
スクリプトから API を呼び出し、応答を変数として保存します。 そうすると、データを外部に保存し、データの傾向を分析できるようになります。
次の例では、JavaScript に Octokit クライアントを使っています。 cURL や GitHub CLI などの他のメソッドを使って API を呼び出すこともできます。
例
次の点に注意してください。
- "YOUR_TOKEN" は personal access token に置き換えます。
- "YOUR_ORG" は organization 名 (
octo-org
など) に置き換えます。
// Import Octokit import { Octokit } from "octokit"; // Set your token and organization const octokit = new Octokit({ auth: 'YOUR_TOKEN' }); const org = 'YOUR_ORG'; // Set other variables if required for the endpoint you're using /* const team = 'YOUR_TEAM'; const enterprise = 'YOUR_ENTERPRISE'; const entTeam = 'YOUR_ENTERPRISE_TEAM'; */ // Call the API async function orgMetrics() { const resp = await octokit.request(`GET /orgs/${org}/copilot/metrics`, { org: 'ORG', headers: { 'X-GitHub-Api-Version': '2022-11-28' } }); const copilotUsage = resp.data; console.log(copilotUsage); } // Call the function orgMetrics();
import { Octokit } from "octokit";
Import Octokit
const octokit = new Octokit({
auth: 'YOUR_TOKEN'
});
const org = 'YOUR_ORG';
Set your token and organization
/*
const team = 'YOUR_TEAM';
const enterprise = 'YOUR_ENTERPRISE';
const entTeam = 'YOUR_ENTERPRISE_TEAM';
*/
Set other variables if required for the endpoint you're using
async function orgMetrics() {
const resp = await octokit.request(`GET /orgs/${org}/copilot/metrics`, {
org: 'ORG',
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
});
const copilotUsage = resp.data;
console.log(copilotUsage);
}
Call the API
orgMetrics();
Call the function
// Import Octokit
import { Octokit } from "octokit";
// Set your token and organization
const octokit = new Octokit({
auth: 'YOUR_TOKEN'
});
const org = 'YOUR_ORG';
// Set other variables if required for the endpoint you're using
/*
const team = 'YOUR_TEAM';
const enterprise = 'YOUR_ENTERPRISE';
const entTeam = 'YOUR_ENTERPRISE_TEAM';
*/
// Call the API
async function orgMetrics() {
const resp = await octokit.request(`GET /orgs/${org}/copilot/metrics`, {
org: 'ORG',
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
});
const copilotUsage = resp.data;
console.log(copilotUsage);
}
// Call the function
orgMetrics();
スクリプトをローカルで実行する
スクリプトをローカルでテストするには、copilot.mjs
という名前でファイルを保存し、node copilot.mjs
を実行します。
Important
.mjs というファイルの種類が重要です。 import { Octokit }
ステートメントは、通常の .js
ファイルでは機能しない可能性があります。
ターミナルには、次のような JSON 配列の出力が表示されます。
[
{
date: '2024-11-07',
copilot_ide_chat: { editors: [Array], total_engaged_users: 14 },
total_active_users: 28,
copilot_dotcom_chat: { models: [Array], total_engaged_users: 4 },
total_engaged_users: 28,
copilot_dotcom_pull_requests: { total_engaged_users: 0 },
copilot_ide_code_completions: { editors: [Array], total_engaged_users: 22 }
},
...
3.データを格納する
28 日を超える傾向を分析するには、次のことを行う必要があります。
- cron ジョブまたはスケジュールされた GitHub Actions ワークフローを使って、毎日 API を呼び出します。
- データは、ローカルに、または MySQL などのデータベース サービスを使って保存します。
- データのクエリを実行して、時間の経過に伴う傾向を特定します。
例
この例では、データをローカルの .json
ファイルに保存します。 これを行うには、ファイルを操作するためにいくつかのモジュールをインポートし、応答データを保存するために orgMetrics
関数を更新します。
この関数を使うと、ファイル内の古いデータを上書きせずに、毎日返される新しいデータを保存できます。
新しいステップには、太字で注釈が付けられています。
// Import Octokit import { Octokit } from "octokit"; // **Import modules for working with files** import path from 'path'; import fs from 'fs'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; // **Declare variables for working with files** const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Set your token and organization const octokit = new Octokit({ auth: 'YOUR_TOKEN' }); const org = 'YOUR_ORG'; // Call the API async function orgMetrics() { const resp = await octokit.request(`GET /orgs/${org}/copilot/metrics`, { org: 'ORG', headers: { 'X-GitHub-Api-Version': '2022-11-28' } }); const copilotUsage = resp.data; // **Define the path to the local file where data will be stored** const dataFilePath = path.join(__dirname, 'copilotMetricsData.json'); // **Read existing data from the file, if it exists** let existingData = []; if (fs.existsSync(dataFilePath)) { const fileContent = fs.readFileSync(dataFilePath, 'utf8'); existingData = JSON.parse(fileContent); } // **Filter out the new data that is not already in the existing data** const newData = copilotUsage.filter(entry => !existingData.some(existingEntry => existingEntry.date === entry.date)); // **Append new data to the existing data** if (newData.length > 0) { existingData = existingData.concat(newData); // **Save the updated data back to the file** fs.writeFileSync(dataFilePath, JSON.stringify(existingData, null, 2)); console.log(`Saved ${newData.length} new entries.`); } else { console.log('No new data to save.'); } } // Call the function orgMetrics();
import { Octokit } from "octokit";
Import Octokit
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
Import modules for working with files
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
Declare variables for working with files
const octokit = new Octokit({
auth: 'YOUR_TOKEN'
});
const org = 'YOUR_ORG';
Set your token and organization
async function orgMetrics() {
const resp = await octokit.request(`GET /orgs/${org}/copilot/metrics`, {
org: 'ORG',
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
});
const copilotUsage = resp.data;
Call the API
const dataFilePath = path.join(__dirname, 'copilotMetricsData.json');
Define the path to the local file where data will be stored
let existingData = [];
if (fs.existsSync(dataFilePath)) {
const fileContent = fs.readFileSync(dataFilePath, 'utf8');
existingData = JSON.parse(fileContent);
}
Read existing data from the file, if it exists
const newData = copilotUsage.filter(entry => !existingData.some(existingEntry => existingEntry.date === entry.date));
Filter out the new data that is not already in the existing data
if (newData.length > 0) {
existingData = existingData.concat(newData);
Append new data to the existing data
fs.writeFileSync(dataFilePath, JSON.stringify(existingData, null, 2));
console.log(`Saved ${newData.length} new entries.`);
} else {
console.log('No new data to save.');
}
}
Save the updated data back to the file
orgMetrics();
Call the function
// Import Octokit
import { Octokit } from "octokit";
// **Import modules for working with files**
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
// **Declare variables for working with files**
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Set your token and organization
const octokit = new Octokit({
auth: 'YOUR_TOKEN'
});
const org = 'YOUR_ORG';
// Call the API
async function orgMetrics() {
const resp = await octokit.request(`GET /orgs/${org}/copilot/metrics`, {
org: 'ORG',
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
});
const copilotUsage = resp.data;
// **Define the path to the local file where data will be stored**
const dataFilePath = path.join(__dirname, 'copilotMetricsData.json');
// **Read existing data from the file, if it exists**
let existingData = [];
if (fs.existsSync(dataFilePath)) {
const fileContent = fs.readFileSync(dataFilePath, 'utf8');
existingData = JSON.parse(fileContent);
}
// **Filter out the new data that is not already in the existing data**
const newData = copilotUsage.filter(entry => !existingData.some(existingEntry => existingEntry.date === entry.date));
// **Append new data to the existing data**
if (newData.length > 0) {
existingData = existingData.concat(newData);
// **Save the updated data back to the file**
fs.writeFileSync(dataFilePath, JSON.stringify(existingData, null, 2));
console.log(`Saved ${newData.length} new entries.`);
} else {
console.log('No new data to save.');
}
}
// Call the function
orgMetrics();
スクリプトをローカルで実行する
node copilot.mjs
を使用してスクリプトを実行すると、ディレクトリに copilotMetricsData.json
という新しいファイルが作成されます。 このファイルには、API 応答からのデータが格納されます。
明日もう一度スクリプトを実行すると、新しい 1 日分のデータのみがこのファイルに保存されます。
4.傾向を分析する
API からのデータを操作して、過去 28 日間の傾向を特定することができます。また、以前の API 呼び出しからのデータを保存している場合は、より長期間の傾向を特定することができます。
例
次の例では、orgMetrics
関数を更新して、1 週間あたりのアクティブ ユーザーとエンゲージメント ユーザーの合計数と平均数を抽出しています。 次に、そのデータを使って時間の経過に伴う変化を追跡できます。 この例では、API から直接返されたデータを使っており、格納データは必要ありません。
新しいステップには、太字で注釈が付けられています。
// Call the API async function orgMetrics() { const resp = await octokit.request(`GET /orgs/${org}/copilot/metrics`, { org: 'ORG', headers: { 'X-GitHub-Api-Version': '2022-11-28' } }); const copilotUsage = resp.data; // **Create an object to store data for each week** let userTrends ={ week1: { days:0, activeUsers:0, engagedUsers:0, }, week2: { days:0, activeUsers:0, engagedUsers:0, }, week3: { days:0, activeUsers:0, engagedUsers:0, }, week4: { days:0, activeUsers:0, engagedUsers:0, }, }; // **Iterate over the data** for (let i =0; i<copilotUsage.length; i++) { // **Determine the week number (1-4) based on the index** const week = Math.ceil((i+1)/7); // **Increment userTrends for the current week** userTrends[`week${week}`].days += 1; userTrends[`week${week}`].activeUsers += copilotUsage[i].total_active_users; userTrends[`week${week}`].engagedUsers += copilotUsage[i].total_engaged_users; } // **Calculate the average number of active and engaged users per day for each week, rounded to two decimal places** for (const week in userTrends) { userTrends[week].avgActiveUsers = (userTrends[week].activeUsers / userTrends[week].days).toFixed(2); userTrends[week].avgEngagedUsers = (userTrends[week].engagedUsers / userTrends[week].days).toFixed(2); } // Output to the console console.log(userTrends); }
async function orgMetrics() {
const resp = await octokit.request(`GET /orgs/${org}/copilot/metrics`, {
org: 'ORG',
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
});
const copilotUsage = resp.data;
Call the API
let userTrends ={
week1: {
days:0,
activeUsers:0,
engagedUsers:0,
},
week2: {
days:0,
activeUsers:0,
engagedUsers:0,
},
week3: {
days:0,
activeUsers:0,
engagedUsers:0,
},
week4: {
days:0,
activeUsers:0,
engagedUsers:0,
},
};
Create an object to store data for each week
for (let i =0; i<copilotUsage.length; i++) {
Iterate over the data
const week = Math.ceil((i+1)/7);
Determine the week number (1-4) based on the index
userTrends[`week${week}`].days += 1;
userTrends[`week${week}`].activeUsers += copilotUsage[i].total_active_users;
userTrends[`week${week}`].engagedUsers += copilotUsage[i].total_engaged_users;
}
Increment userTrends for the current week
for (const week in userTrends) {
userTrends[week].avgActiveUsers = (userTrends[week].activeUsers / userTrends[week].days).toFixed(2);
userTrends[week].avgEngagedUsers = (userTrends[week].engagedUsers / userTrends[week].days).toFixed(2);
}
Calculate the average number of active and engaged users per day for each week, rounded to two decimal places
console.log(userTrends);
}
Output to the console
// Call the API
async function orgMetrics() {
const resp = await octokit.request(`GET /orgs/${org}/copilot/metrics`, {
org: 'ORG',
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
});
const copilotUsage = resp.data;
// **Create an object to store data for each week**
let userTrends ={
week1: {
days:0,
activeUsers:0,
engagedUsers:0,
},
week2: {
days:0,
activeUsers:0,
engagedUsers:0,
},
week3: {
days:0,
activeUsers:0,
engagedUsers:0,
},
week4: {
days:0,
activeUsers:0,
engagedUsers:0,
},
};
// **Iterate over the data**
for (let i =0; i<copilotUsage.length; i++) {
// **Determine the week number (1-4) based on the index**
const week = Math.ceil((i+1)/7);
// **Increment userTrends for the current week**
userTrends[`week${week}`].days += 1;
userTrends[`week${week}`].activeUsers += copilotUsage[i].total_active_users;
userTrends[`week${week}`].engagedUsers += copilotUsage[i].total_engaged_users;
}
// **Calculate the average number of active and engaged users per day for each week, rounded to two decimal places**
for (const week in userTrends) {
userTrends[week].avgActiveUsers = (userTrends[week].activeUsers / userTrends[week].days).toFixed(2);
userTrends[week].avgEngagedUsers = (userTrends[week].engagedUsers / userTrends[week].days).toFixed(2);
}
// Output to the console
console.log(userTrends);
}
スクリプトをローカルで実行する
node copilot.mjs
を使ってスクリプトを実行すると、ターミナルに次のような出力が表示されます。
{
week1: {
days: 7,
activeUsers: 174,
engagedUsers: 174,
avgActiveUsers: '24.86',
avgEngagedUsers: '24.86'
},
week2: {
days: 7,
activeUsers: 160,
engagedUsers: 151,
avgActiveUsers: '22.86',
avgEngagedUsers: '21.57'
},
week3: {
days: 7,
activeUsers: 134,
engagedUsers: 123,
avgActiveUsers: '19.14',
avgEngagedUsers: '17.57'
},
week4: {
days: 6,
activeUsers: 143,
engagedUsers: 132,
avgActiveUsers: '23.83',
avgEngagedUsers: '22.00'
}
}