ただし、ここでの主な課題は、このデータを手動で分析する必要があることです。このアクティビティを監視するたびに、レポートにアクセスして再計算し、データを分析する必要があります。履歴分析を行う必要がある場合は、データをエクスポートし、外部ツールで数値を計算する必要があります。
レポート作成ツールとしてのワークフローの使用
ワークフローは、計算されたレポートデータに直接アクセスできませんが、課題やワークアイテムの形式で生データにアクセスできます。スケジュールルールを使用すると、必要なものを定期的に報告し、チームリーダーでも従業員でもメールで送信できます。
最初に、課題の分析に使用できるコアモジュールから始めます。このスクリプトは、特定の期間内に記録された特定のプロジェクトの特定の担当者のすべての作業項目を検索します。このモジュールは他のワークフロールールで参照されているため、これを work-items という別のカスタムスクリプトとして保存します。
const search = require('@jetbrains/youtrack-scripting-api/search');
const dates = require('@jetbrains/youtrack-scripting-api/date-time');
function formatter(timestamp) {
return dates.format(timestamp, 'yyyy-MM-dd');
}
/**
* @param {User} [author] work items author
* @param {Project} [project] project to get issue from
* @param {Number} [from] starting date in ms from the epoch start
* @param {Number} [to] ending date in ms from the epoch start
* @return {[WorkItem]} list of work items matching the parameters
*/
const fetchWorkItems = function (author, project, from, to) {
// Generate a search string to find issues,
// where at least one work item was added by `author` between `from` and `to`:
let searchQuery = 'work author: ' + author.login + ' ';
searchQuery += 'work date: ' + formatter(from) + ' .. ' + formatter(to);
// Now we can traverse over these issues in a `project`
// and choose the work items we need:
const items = [];
const issues = search.search(project, searchQuery);
issues.forEach(function (issue) {
issue.workItems.forEach(function (item) {
if (item.author.login === author.login &&
item.date >= from && item.date <= to) {
items.push(item);
}
})
});
// Return the array:
return items;
};
exports.fetchWorkItems = fetchWorkItems;
次に、先週、各開発者がどれだけの作業をログに記録したか、チームのリーダーが次の質問に回答できるようにします。担当者フィールドから開発者のリストとして値のセットを抽出し、それぞれの作業項目を取得し、記録された時間と必要な作業時間(開発者ごとに 40 時間など)の差を計算します。
スケジュールに従って実行される他のワークフローで見たように、このルールはアンカーの課題を使用します。アンカー課題を使用すると、課題が属するプロジェクトをコンテキストにプルし、プロジェクト内の他の課題を反復処理できます。また、スケジュールされた実行ごとにルールが 1 回だけ実行されるようにします。
アンカーの課題の場合は、「この課題を削除しないでください ! 」などの説明を含む課題を作成し、解決済みの状態に設定します。その後、スケジュール上のルールの search プロパティでその ID を参照できます。これスケジュールされた時間ごとに 1 回だけルールが実行されるようにします。この手法が次のルールに適用されていることがわかります。
const entities = require('@jetbrains/youtrack-scripting-api/entities');
const wi = require('./work-items');
const DAY_IN_MS = 24 * 60 * 60 * 1000;
const HOURS_TO_WORK_A_WEEK = 40;
exports.rule = entities.Issue.onSchedule({
title: 'Send report to the project lead every Monday',
cron: '0 0 10 ? * MON',
search: '#WI-1', // // TODO: replace with the ID of an anchor issue
action: (ctx) => {
const project = ctx.issue.project;
// Calculate start and end of the last week:
let from = new Date();
from.setHours(0, 0, 0, 0); // the start of this day
from = from.getTime() - 7 * DAY_IN_MS; // the start of last Monday
const to = from + 7 * DAY_IN_MS - 1; // the end of last Sunday
// Get a list of assignees from the Assignee field in the project,
// get a list of work items for each of them, and calculate sum of durations
// for the work items reported by each assignee:
const durations = {};
const assignees = ctx.Assignee.values;
assignees.forEach(function (assignee) {
const items = wi.fetchWorkItems(assignee, project, from, to);
let duration = 0; // duration in minutes
items.forEach(function (item) {
duration += item.duration;
});
durations[assignee.login] = duration / 60;
});
// Create email content:
const subject = '[YouTrack, Report] Report of work done last week';
let body = 'Here is the report for last week: \n\n';
assignees.forEach(function (assignee) {
const duration = durations[assignee.login];
let text = assignee.fullName + ' worked for ' + duration + ' hour(s)';
if (duration > HOURS_TO_WORK_A_WEEK) {
text += ' (overtime for ' + (duration - HOURS_TO_WORK_A_WEEK) +
' hour(s)).\n';
} else if (duration < HOURS_TO_WORK_A_WEEK) {
text += ' (downtime for ' + (HOURS_TO_WORK_A_WEEK - duration) +
' hour(s)).\n';
} else {
text += '.\n';
}
body += text;
});
body += '\nSincerely yours, YouTrack\n';
// Send email to the project lead:
project.leader.notify(subject, body);
},
requirements: {
Assignee: {
type: entities.User.fieldType
}
}
});
このルールの素晴らしい点は、高度にカスタマイズできることです。この機能をプッシュできる方向のいくつかを以下に示します。
担当者フィールドの値のセットを使用する代わりに、1 つ以上のグループのメンバーシップに基づいて開発者のリストを生成します。
複数のプロジェクトからデータを取得し、それぞれに費やした時間を計算し、プロジェクトまたは開発者ごとにグループ化した時間をグループ化します。
共通の定数を使用する代わりに、開発者ごとに必要な作業時間をマッピングします。
2 番目の例として、週の終わりに記録された作業量が必要な作業期間より少ない場合に、開発者にリマインダーを送信します。
const entities = require('@jetbrains/youtrack-scripting-api/entities');
const wi = require('./work-items');
const DAY_IN_MS = 24 * 60 * 60 * 1000;
const HOURS_TO_WORK_A_WEEK = 40;
exports.rule = entities.Issue.onSchedule({
title: 'Remind developers on Friday if they have not logged enough work',
cron: '0 0 16 ? * FRI',
search: '#WI-1', // // TODO: replace with ID of an anchor issue
action: (ctx) => {
const project = ctx.issue.project;
// Calculate start and end of this week:
const to = new Date(); // current moment
let from = new Date(to - 4 * DAY_IN_MS); // Monday 16:00
from.setHours(0, 0, 0, 0);
from = from.getTime(); // the start of last Monday
// Get a list of assignees from the Assignee field in the project,
// get a list of work items for each of them, and calculate sum of durations
// for the work items reported by each assignee:
const durations = {};
const assignees = ctx.Assignee.values;
assignees.forEach(function (assignee) {
const items = wi.fetchWorkItems(assignee, project, from, to);
let duration = 0; // duration in minutes
items.forEach(function (item) {
duration += item.duration;
});
durations[assignee.login] = duration / 60;
});
// Send emails in case of work is not yet done:
assignees.forEach(function (assignee) {
const duration = durations[assignee.login];
if (duration < HOURS_TO_WORK_A_WEEK) {
const subject = '[YouTrack, Reminder] Work done this week';
let body = 'Hey ' + assignee.fullName + ',\n\n';
body +=
'Looks like you have forgot to log some work: you have worked on ' +
project.name + ' for ' + duration + ' hour(s) instead of ' +
HOURS_TO_WORK_A_WEEK + ' required for you.\n';
body += '\nSincerely yours, YouTrack\n';
assignee.notify(subject, body);
}
});
},
requirements: {
Assignee: {
type: entities.User.fieldType
}
}
});
前のスクリプトを拡張するための同じアイデアがここにも当てはまります。作業項目にアクセスする機能により、請求可能な時間だけでなく、チームの集団速度や各開発者の相対的なパフォーマンスなど、他の数値特性も計算できます。