evmtools-node
Version:
このライブラリは、プライムブレインズ社で利用している「進捗管理ツール(Excel)」ファイルを読み込み、 プロジェクトの進捗状況や要員別の作業量を可視化するためのライブラリです。
336 lines • 14.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Project = void 0;
const tidy_1 = require("@tidyjs/tidy");
const common_1 = require("../common");
const TaskService_1 = require("./TaskService");
const calcUtils_1 = require("../common/calcUtils");
const logger_1 = require("../logger");
class Project {
constructor(_taskNodes, _baseDate, _holidayDatas, _startDate, _endDate, _name) {
this._taskNodes = _taskNodes;
this._baseDate = _baseDate;
this._holidayDatas = _holidayDatas;
this._startDate = _startDate;
this._endDate = _endDate;
this._name = _name;
this.logger = (0, logger_1.getLogger)('domain/Project');
this._taskService = new TaskService_1.TaskService();
this.printAndGetRawData = (printRowNum) => {
this.logger.info(`プロジェクト名: ${this._name}`);
this.logger.info(`開始日: ${(0, common_1.dateStr)(this._startDate)}`);
this.logger.info(`終了日: ${(0, common_1.dateStr)(this._endDate)}`);
this.logger.info(`基準日: ${(0, common_1.dateStr)(this._baseDate)}`);
// console.table(this._taskNodes)
const taskRows = this.toTaskRows();
const rows = taskRows.map((taskRow) => {
const {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
calculatePV,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
calculatePVs,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
plotMap,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
checkStartEndDateAndPlotMap, startDate, endDate, actualStartDate, actualEndDate, expectedProgressDate, ...rest // ココのデータだけが出力される
} = taskRow;
return {
...rest,
予定開始日: (0, common_1.dateStr)(startDate),
予定終了日: (0, common_1.dateStr)(endDate),
実績開始日: (0, common_1.dateStr)(actualStartDate),
実績終了日: (0, common_1.dateStr)(actualEndDate),
進捗応当日: (0, common_1.dateStr)(expectedProgressDate),
};
});
// ユーザ入力値か、未指定なら全部。入力値が大きいときも全部
// const num = printRowNum && printRowNum <= rows.length ? printRowNum : rows.length
const taskCount = rows.length;
this.logger.info(`タスク数:${taskCount}件`);
const numToShow = Math.min(printRowNum ?? taskCount, taskCount);
this.logger.info(`先頭${numToShow}行データ:`);
console.table(rows.slice(0, numToShow));
return rows;
};
}
get baseDate() {
return this._baseDate;
}
get taskNodes() {
return this._taskNodes;
}
get startDate() {
return this._startDate;
}
get endDate() {
return this._endDate;
}
get name() {
return this._name;
}
get holidayDatas() {
return this._holidayDatas;
}
get length() {
return this.toTaskRows().length;
}
getTask(id) {
if (!this._cachedTaskMap) {
this._cachedTaskMap = new Map(this.toTaskRows().map((row) => [row.id, row]));
}
return this._cachedTaskMap.get(id);
}
toTaskRows() {
if (!this._cachedTaskRows) {
this._cachedTaskRows = this._taskService.convertToTaskRows(this._taskNodes);
}
return this._cachedTaskRows;
// return this._taskService.convertToTaskRows(this._taskNodes)
}
/**
* 親を遡って、名前を"/"でjoinする
* @param task 子のタスク
* @param taskMap 親のタスクIDも存在する、<id,TaskRow>なMap
* @returns
*/
getFullTaskName(task) {
const names = [];
let current = task;
while (current) {
names.unshift(current.name); // 先頭に名前追加
current = current.parentId ? this.getTask(current.parentId) : undefined;
}
return names.join('/');
}
/**
* 指定された期間、担当者のタスク配列を返す。親タスクは除外しています。
* @param fromDate
* @param toDate
* @param assignee
* @returns
*/
getTaskRows(fromDate, toDate, assignee) {
const baseDates = (0, common_1.generateBaseDates)(fromDate, toDate ?? fromDate);
const taskRows = this.toTaskRows().filter((taskRow) => taskRow.isLeaf);
return taskRows.filter((taskRow) => {
// const hasPV = baseDates.some((baseDate) => taskRow.calculatePV(baseDate) !== 0) //0じゃないヤツが、その日にあるタスク
const hasPV = baseDates.some((baseDate) => {
const pv = taskRow.calculatePV(baseDate);
return typeof pv === 'number' && pv !== 0;
});
const assigneeMatch = !assignee || taskRow.assignee === assignee;
return hasPV && assigneeMatch;
});
}
/**
* Project単位の統計情報を返す
* @param project
* @returns
*/
get statisticsByProject() {
const name = this._name;
const baseDate = this._baseDate;
const startDate = this._startDate; // Date|undefinedだけど、実際はほぼ確実に、存在する(タスクが0コとか)
const endDate = this._endDate; // Date|undefinedだけど、実際はほぼ確実に、存在する(タスクが0コとか)
const rows = this.toTaskRows();
const result = (0, tidy_1.tidy)(rows, (0, tidy_1.filter)((row) => row.isLeaf), //フォルダの情報は不要
(0, tidy_1.summarize)({
projectName: () => name,
startDate: () => (0, common_1.dateStr)(startDate),
endDate: () => (0, common_1.dateStr)(endDate),
totalTasksCount: (group) => group.length,
totalWorkloadExcel: sumWorkload, // Excel工数(task#workload) の 足し算
totalWorkloadCalculated: (group) => sumCalculatePVs(group, endDate), // endDate時の、計算、累積pv(したのヤツ) の、足し算
averageWorkload: averageWorkload,
baseDate: () => (0, common_1.dateStr)(baseDate),
totalPvExcel: sumPVs, // Excel累積pv(TaskRow#pv) の足し算
totalPvCalculated: (group) => sumCalculatePVs(group, baseDate), // 計算、累積pv(TaskRow#calculatePVs(baseDate)) の、足し算
totalEv: sumEVs, // Excel累積Ev(TaskRow#ev) の足し算
spi: (group) => calculateSPI(group, baseDate),
}));
// console.table(result)
return result;
}
get statisticsByName() {
const baseDate = this._baseDate;
const endDate = this._endDate;
const rows = this.toTaskRows();
const result = (0, tidy_1.tidy)(rows,
// mutate({
// assignee: (row) => row.assignee?.trim() ?? '', // 🔧 trim()を適用
// }),
(0, tidy_1.filter)((row) => row.isLeaf), //フォルダの情報は不要
(0, tidy_1.groupBy)('assignee', [
(0, tidy_1.summarize)({
totalTasksCount: (group) => group.length,
totalWorkloadExcel: sumWorkload,
totalWorkloadCalculated: (group) => sumCalculatePVs(group, endDate),
averageWorkload: averageWorkload,
baseDate: () => (0, common_1.dateStr)(baseDate),
totalPvExcel: sumPVs,
totalPvCalculated: (group) => sumCalculatePVs(group, baseDate),
totalEv: sumEVs,
spi: (group) => calculateSPI(group, baseDate),
}),
]));
// console.table(result)
return result;
}
/**
* LongData形式のPV情報を返す
* @param calcPVS 累積が欲しいときはtrue、デフォルトはfalse
* @returns LongData[]
*/
_internalPvByProjectLong(calcPVS = false) {
// const baseDate = project.baseDate
const from = this._startDate;
const to = this._endDate;
// const projectName = this._name
if (!(from && to)) {
throw new Error('fromかtoが取得できませんでした');
}
const baseDates = (0, common_1.generateBaseDates)(from, to);
const rows = this.toTaskRows();
const longFormat = [];
for (const baseDate of baseDates) {
const label = (0, common_1.dateStr)(baseDate);
const result = (0, tidy_1.tidy)(rows, (0, tidy_1.filter)((row) => row.isLeaf), //フォルダの情報は不要
// filter((row: TaskRow) => row.assignee !== undefined),
(0, tidy_1.summarize)({
[`${label}`]: (group) => calcPVS
? sumCalculatePVs(group, baseDate)
: sumCalculatePV(group, baseDate),
// 基準日ごとに、担当者でグルーピングされたPVデータを足している
}));
// console.table(result)
for (const row of result) {
const name = (row.assignee ?? '(未割当)');
longFormat.push({
assignee: name,
baseDate: label,
value: row[label],
});
}
}
return longFormat;
}
/**
* Projectごともしくはヒトごとのデータについて、ひと単位の横並びデータに並び替える
* Excelに表示するなどはこちらが良い
* @param longDatas
* @returns
*/
_toWideFormat(longDatas) {
const wideMap = new Map();
for (const { assignee, baseDate, value } of longDatas) {
// const mapKey = `${assignee}::${fromClass}->${toClass}`
// assigneeごとに、baseDateプロパティを追加していく(pvデータを横並びにしたい)
const mapKey = assignee;
if (!wideMap.has(mapKey)) {
wideMap.set(mapKey, { assignee });
}
wideMap.get(mapKey)[baseDate] = value;
}
return Array.from(wideMap.values());
}
_internalPvByProject(calcPVS = false) {
const longDatas = this._internalPvByProjectLong(calcPVS);
return this._toWideFormat(longDatas);
}
get pvByProjectLong() {
return this._internalPvByProjectLong();
}
get pvsByProjectLong() {
return this._internalPvByProjectLong(true);
}
get pvByProject() {
return this._internalPvByProject();
}
get pvsByProject() {
return this._internalPvByProject(true);
}
_internalPvByNameLong(calcPVS = false) {
const from = this._startDate;
const to = this._endDate;
if (!(from && to)) {
throw new Error('fromかtoが取得できませんでした');
}
const baseDates = (0, common_1.generateBaseDates)(from, to);
const rows = this.toTaskRows();
const longFormat = [];
for (const baseDate of baseDates) {
const label = (0, common_1.dateStr)(baseDate);
const result = (0, tidy_1.tidy)(rows, (0, tidy_1.filter)((row) => row.isLeaf), //フォルダの情報は不要
// filter((row: TaskRow) => row.assignee !== undefined),
(0, tidy_1.groupBy)('assignee', [
(0, tidy_1.summarize)({
[`${label}`]: (group) => calcPVS
? sumCalculatePVs(group, baseDate)
: sumCalculatePV(group, baseDate), // 基準日ごとに、担当者でグルーピングされたPVデータを足している
}),
]));
// console.table(result)
for (const row of result) {
const name = (row.assignee ?? '(未割当)');
longFormat.push({
assignee: name,
baseDate: label,
value: row[label],
});
}
}
return longFormat;
}
_internalPvByName(calcPVS = false) {
const longDatas = this._internalPvByNameLong(calcPVS);
return this._toWideFormat(longDatas);
}
get pvByNameLong() {
return this._internalPvByNameLong();
}
get pvsByNameLong() {
return this._internalPvByNameLong(true);
}
get pvByName() {
return this._internalPvByName();
}
get pvsByName() {
return this._internalPvByName(true);
}
isHoliday(date) {
return (0, common_1.isHoliday)(date, this);
}
}
exports.Project = Project;
const sumWorkload = (group) => (0, common_1.sum)(group.map((d) => d.workload ?? 0));
const averageWorkload = (group) => (0, common_1.average)(group.map((d) => d.workload ?? 0));
const sumCalculatePV = (group, baseDate) => (0, common_1.sum)(group.map((d) => d.calculatePV(baseDate) ?? 0), 3);
const sumCalculatePVs = (group, baseDate) => (0, common_1.sum)(group.map((d) => d.calculatePVs(baseDate) ?? 0), 3);
const sumPVs = (group) => (0, common_1.sum)(group.map((d) => d.pv ?? 0), 3); // 基準日ごとに、担当者でグルーピングされたPVデータを足している
const sumEVs = (group) => (0, common_1.sum)(group.map((d) => d.ev ?? 0), 3);
const calculateSPI = (group, baseDate) => {
const ev = sumEVs(group);
const pv = sumCalculatePVs(group, baseDate);
return (0, calcUtils_1.calcRate)(ev, pv);
};
// /**
// * タスク情報
// */
// private nu.mine.kino.entity.TaskInformation[] taskInformations;
// /**
// * プロジェクト開始日
// */
// private java.util.Date projectStartDate;
// /**
// * プロジェクト終了日
// */
// private java.util.Date projectEndDate;
// /**
// * 基準日
// */
// private java.util.Date baseDate;
// /**
// * 休日カレンダー
// */
// private nu.mine.kino.entity.Holiday[] holidays;
//# sourceMappingURL=Project.js.map