evmtools-node 0.0.25 → 0.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/jest.config.js CHANGED
@@ -1,26 +1,26 @@
1
- /** @type {import('ts-jest').JestConfigWithTsJest} */
2
- module.exports = {
3
- preset: 'ts-jest',
4
- testEnvironment: 'node',
5
- roots: ['<rootDir>/src'],
6
- testMatch: ['**/__tests__/**/*.test.ts', '**/*.test.ts'],
7
- testPathIgnorePatterns: [
8
- '/node_modules/',
9
- 'cli-shebang\\.test\\.ts$', // リリース検証用(リグレッションテスト対象外)
10
- ],
11
- moduleFileExtensions: ['ts', 'js', 'json'],
12
- collectCoverageFrom: [
13
- 'src/**/*.ts',
14
- '!src/**/*.d.ts',
15
- '!src/**/index.ts',
16
- '!src/presentation/**', // CLIは除外
17
- ],
18
- coverageDirectory: 'coverage',
19
- coverageReporters: [
20
- 'text', // コンソール出力
21
- 'text-summary', // サマリー出力
22
- 'html', // HTMLレポート
23
- 'lcov', // CI/CD用(Codecovなど)
24
- ],
25
- verbose: true,
26
- }
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
2
+ module.exports = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+ roots: ['<rootDir>/src'],
6
+ testMatch: ['**/__tests__/**/*.test.ts', '**/*.test.ts'],
7
+ testPathIgnorePatterns: [
8
+ '/node_modules/',
9
+ 'cli-shebang\\.test\\.ts$', // リリース検証用(リグレッションテスト対象外)
10
+ ],
11
+ moduleFileExtensions: ['ts', 'js', 'json'],
12
+ collectCoverageFrom: [
13
+ 'src/**/*.ts',
14
+ '!src/**/*.d.ts',
15
+ '!src/**/index.ts',
16
+ '!src/presentation/**', // CLIは除外
17
+ ],
18
+ coverageDirectory: 'coverage',
19
+ coverageReporters: [
20
+ 'text', // コンソール出力
21
+ 'text-summary', // サマリー出力
22
+ 'html', // HTMLレポート
23
+ 'lcov', // CI/CD用(Codecovなど)
24
+ ],
25
+ verbose: true,
26
+ }
package/package.json CHANGED
@@ -1,111 +1,111 @@
1
- {
2
- "name": "evmtools-node",
3
- "version": "0.0.25",
4
- "description": "このライブラリは、プライムブレインズ社で利用している「進捗管理ツール(Excel)」ファイルを読み込み、 プロジェクトの進捗状況や要員別の作業量を可視化するためのライブラリです。",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "exports": {
8
- "./common": {
9
- "types": "./dist/common/index.d.ts",
10
- "import": "./dist/common/index.js"
11
- },
12
- "./domain": {
13
- "types": "./dist/domain/index.d.ts",
14
- "import": "./dist/domain/index.js"
15
- },
16
- "./infrastructure": {
17
- "types": "./dist/infrastructure/index.d.ts",
18
- "import": "./dist/infrastructure/index.js"
19
- },
20
- "./presentation": {
21
- "types": "./dist/presentation/index.d.ts",
22
- "import": "./dist/presentation/index.js"
23
- },
24
- "./usecase": {
25
- "types": "./dist/usecase/index.d.ts",
26
- "import": "./dist/usecase/index.js"
27
- },
28
- "./logger": {
29
- "types": "./dist/logger.d.ts",
30
- "import": "./dist/logger.js"
31
- },
32
- "./project": {
33
- "types": "./dist/project/index.d.ts",
34
- "import": "./dist/project/index.js"
35
- },
36
- "./resource": {
37
- "types": "./dist/resource/index.d.ts",
38
- "import": "./dist/resource/index.js"
39
- }
40
- },
41
- "bin": {
42
- "pbevm-show-project": "./dist/presentation/cli-pbevm-show-project.js",
43
- "pbevm-diff": "./dist/presentation/cli-pbevm-diff.js",
44
- "pbevm-show-pv": "./dist/presentation/cli-pbevm-show-pv.js",
45
- "pbevm-show-resourceplan": "./dist/resource/presentation/cli-pbevm-show-resourceplan.js"
46
- },
47
- "scripts": {
48
- "typecheck": "tsc --noEmit",
49
- "pretest": "npm run typecheck",
50
- "test": "jest",
51
- "test:coverage": "jest --coverage",
52
- "lint": "eslint . --ext .ts",
53
- "lint:fix": "eslint . --ext .ts --fix",
54
- "format": "prettier --check 'src/**/*.ts'",
55
- "format:fix": "prettier --write 'src/**/*.ts'",
56
- "clean:modules": "rimraf node_modules pnpm-lock.yaml",
57
- "clean": "rimraf dist",
58
- "tsc": "tsc",
59
- "copy:assets": "cpx \"src/**/*.hbs\" dist",
60
- "build": "npm-run-all clean tsc copy:assets",
61
- "pack": "npm pack",
62
- "prepublishOnly": "npm run build",
63
- "pbevm-show-project": "ts-node ./src/presentation/cli-pbevm-show-project --path now.xlsm",
64
- "pbevm-diff": "ts-node ./src/presentation/cli-pbevm-diff --path now.xlsm --prevPath prev.xlsm",
65
- "pbevm-show-pv": "ts-node ./src/presentation/cli-pbevm-show-pv --path now.xlsm",
66
- "cli-test": "ts-node ./src/presentation/cli-test --excelPath now.xlsm",
67
- "pbevm-summary": "ts-node ./src/presentation/cli-pbevm-summary --path now.xlsm",
68
- "pbevm-show-resourceplan": "ts-node ./src/resource/presentation/cli-pbevm-show-resourceplan"
69
- },
70
- "repository": {
71
- "type": "git",
72
- "url": "git+https://github.com/masatomix/evmtools-node.git"
73
- },
74
- "keywords": [
75
- "typescript",
76
- "javascript"
77
- ],
78
- "author": "Masatomi KINO <masatomix@ki-no.org> (http://qiita.com/masatomix)",
79
- "license": "ISC",
80
- "dependencies": {
81
- "@tidyjs/tidy": "^2.5.2",
82
- "config": "^4.0.0",
83
- "excel-csv-read-write": "^0.2.6",
84
- "handlebars": "^4.7.8",
85
- "iconv-lite": "^0.7.1",
86
- "pino": "^9.7.0",
87
- "ts-node": "^10.9.2",
88
- "yargs": "^17.7.2"
89
- },
90
- "devDependencies": {
91
- "@eslint/js": "^9.28.0",
92
- "@types/config": "^3.3.5",
93
- "@types/iconv-lite": "^0.0.1",
94
- "@types/jest": "^30.0.0",
95
- "@types/node": "^22.15.21",
96
- "@types/xlsx-populate": "github:JanLoebel/types-xlsx-populate",
97
- "@types/yargs": "^17.0.33",
98
- "cpx": "^1.5.0",
99
- "eslint": "^9.28.0",
100
- "eslint-config-prettier": "^10.1.5",
101
- "eslint-plugin-import": "^2.31.0",
102
- "jest": "^30.2.0",
103
- "npm-run-all": "^4.1.5",
104
- "pino-pretty": "^13.0.0",
105
- "prettier": "^3.5.3",
106
- "rimraf": "^6.0.1",
107
- "ts-jest": "^29.4.6",
108
- "typescript": "^5.8.3",
109
- "typescript-eslint": "^8.33.1"
110
- }
111
- }
1
+ {
2
+ "name": "evmtools-node",
3
+ "version": "0.0.26",
4
+ "description": "このライブラリは、プライムブレインズ社で利用している「進捗管理ツール(Excel)」ファイルを読み込み、 プロジェクトの進捗状況や要員別の作業量を可視化するためのライブラリです。",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ "./common": {
9
+ "types": "./dist/common/index.d.ts",
10
+ "import": "./dist/common/index.js"
11
+ },
12
+ "./domain": {
13
+ "types": "./dist/domain/index.d.ts",
14
+ "import": "./dist/domain/index.js"
15
+ },
16
+ "./infrastructure": {
17
+ "types": "./dist/infrastructure/index.d.ts",
18
+ "import": "./dist/infrastructure/index.js"
19
+ },
20
+ "./presentation": {
21
+ "types": "./dist/presentation/index.d.ts",
22
+ "import": "./dist/presentation/index.js"
23
+ },
24
+ "./usecase": {
25
+ "types": "./dist/usecase/index.d.ts",
26
+ "import": "./dist/usecase/index.js"
27
+ },
28
+ "./logger": {
29
+ "types": "./dist/logger.d.ts",
30
+ "import": "./dist/logger.js"
31
+ },
32
+ "./project": {
33
+ "types": "./dist/project/index.d.ts",
34
+ "import": "./dist/project/index.js"
35
+ },
36
+ "./resource": {
37
+ "types": "./dist/resource/index.d.ts",
38
+ "import": "./dist/resource/index.js"
39
+ }
40
+ },
41
+ "bin": {
42
+ "pbevm-show-project": "./dist/presentation/cli-pbevm-show-project.js",
43
+ "pbevm-diff": "./dist/presentation/cli-pbevm-diff.js",
44
+ "pbevm-show-pv": "./dist/presentation/cli-pbevm-show-pv.js",
45
+ "pbevm-show-resourceplan": "./dist/resource/presentation/cli-pbevm-show-resourceplan.js"
46
+ },
47
+ "scripts": {
48
+ "typecheck": "tsc --noEmit",
49
+ "pretest": "npm run typecheck",
50
+ "test": "jest",
51
+ "test:coverage": "jest --coverage",
52
+ "lint": "eslint . --ext .ts",
53
+ "lint:fix": "eslint . --ext .ts --fix",
54
+ "format": "prettier --check 'src/**/*.ts'",
55
+ "format:fix": "prettier --write 'src/**/*.ts'",
56
+ "clean:modules": "rimraf node_modules pnpm-lock.yaml",
57
+ "clean": "rimraf dist",
58
+ "tsc": "tsc",
59
+ "copy:assets": "cpx \"src/**/*.hbs\" dist",
60
+ "build": "npm-run-all clean tsc copy:assets",
61
+ "pack": "npm pack",
62
+ "prepublishOnly": "npm run build",
63
+ "pbevm-show-project": "ts-node ./src/presentation/cli-pbevm-show-project --path now.xlsm",
64
+ "pbevm-diff": "ts-node ./src/presentation/cli-pbevm-diff --path now.xlsm --prevPath prev.xlsm",
65
+ "pbevm-show-pv": "ts-node ./src/presentation/cli-pbevm-show-pv --path now.xlsm",
66
+ "cli-test": "ts-node ./src/presentation/cli-test --excelPath now.xlsm",
67
+ "pbevm-summary": "ts-node ./src/presentation/cli-pbevm-summary --path now.xlsm",
68
+ "pbevm-show-resourceplan": "ts-node ./src/resource/presentation/cli-pbevm-show-resourceplan"
69
+ },
70
+ "repository": {
71
+ "type": "git",
72
+ "url": "git+https://github.com/masatomix/evmtools-node.git"
73
+ },
74
+ "keywords": [
75
+ "typescript",
76
+ "javascript"
77
+ ],
78
+ "author": "Masatomi KINO <masatomix@ki-no.org> (http://qiita.com/masatomix)",
79
+ "license": "ISC",
80
+ "dependencies": {
81
+ "@tidyjs/tidy": "^2.5.2",
82
+ "config": "^4.0.0",
83
+ "excel-csv-read-write": "^0.2.6",
84
+ "handlebars": "^4.7.8",
85
+ "iconv-lite": "^0.7.1",
86
+ "pino": "^9.7.0",
87
+ "ts-node": "^10.9.2",
88
+ "yargs": "^17.7.2"
89
+ },
90
+ "devDependencies": {
91
+ "@eslint/js": "^9.28.0",
92
+ "@types/config": "^3.3.5",
93
+ "@types/iconv-lite": "^0.0.1",
94
+ "@types/jest": "^30.0.0",
95
+ "@types/node": "^22.15.21",
96
+ "@types/xlsx-populate": "github:JanLoebel/types-xlsx-populate",
97
+ "@types/yargs": "^17.0.33",
98
+ "cpx": "^1.5.0",
99
+ "eslint": "^9.28.0",
100
+ "eslint-config-prettier": "^10.1.5",
101
+ "eslint-plugin-import": "^2.31.0",
102
+ "jest": "^30.2.0",
103
+ "npm-run-all": "^4.1.5",
104
+ "pino-pretty": "^13.0.0",
105
+ "prettier": "^3.5.3",
106
+ "rimraf": "^6.0.1",
107
+ "ts-jest": "^29.4.6",
108
+ "typescript": "^5.8.3",
109
+ "typescript-eslint": "^8.33.1"
110
+ }
111
+ }
@@ -0,0 +1,245 @@
1
+ /**
2
+ * EVM指標サンプル出力
3
+ *
4
+ * 3つのプロジェクト(順調/遅延/失速)を比較して、EVM指標の意味を理解する。
5
+ *
6
+ * 共通設定:
7
+ * - タスク数: 5タスク(各1人日)
8
+ * - 期間: 5稼働日(2025-08-01 〜 2025-08-07)
9
+ * - 基準日: 3日目(2025-08-05、中間地点)
10
+ * - 日々のPV: 1.0人日(理想的な状態)
11
+ *
12
+ * 実行方法(リポジトリルートから):
13
+ * npx ts-node samples/evm-sample-projects.ts
14
+ *
15
+ * 注意: node_modulesが必要なため、worktreeからではなくメインリポジトリから実行すること
16
+ */
17
+
18
+ import { date2Sn } from 'excel-csv-read-write'
19
+ import { Project } from '../src/domain/Project'
20
+ import { TaskNode } from '../src/domain/TaskNode'
21
+
22
+ // ============================================
23
+ // ヘルパー関数
24
+ // ============================================
25
+
26
+ /**
27
+ * plotMapを生成(指定した日のみ)
28
+ */
29
+ function createPlotMapForSingleDay(date: Date): Map<number, boolean> {
30
+ const plotMap = new Map<number, boolean>()
31
+ plotMap.set(date2Sn(date), true)
32
+ return plotMap
33
+ }
34
+
35
+ /**
36
+ * TaskNode生成ヘルパー
37
+ */
38
+ function createTask(
39
+ id: number,
40
+ name: string,
41
+ date: Date,
42
+ progressRate: number
43
+ ): TaskNode {
44
+ const workload = 1 // 各タスク1人日
45
+ const scheduledWorkDays = 1
46
+
47
+ // PV: 基準日(8/5)までのタスクはPV=workload、それ以降は0
48
+ // EV: workload × progressRate
49
+ const baseDate = new Date('2025-08-05')
50
+ const pv = date <= baseDate ? workload : 0
51
+ const ev = workload * progressRate
52
+ const spi = pv > 0 ? ev / pv : 0
53
+
54
+ return new TaskNode(
55
+ id, // sharp
56
+ id, // id
57
+ 1, // level
58
+ name,
59
+ undefined, // assignee
60
+ workload,
61
+ date, // startDate
62
+ date, // endDate (1日タスク)
63
+ undefined, // actualStartDate
64
+ undefined, // actualEndDate
65
+ progressRate,
66
+ scheduledWorkDays,
67
+ pv,
68
+ ev,
69
+ spi,
70
+ undefined, // expectedProgressDate
71
+ undefined, // delayDays
72
+ undefined, // remarks
73
+ undefined, // parentId
74
+ true, // isLeaf
75
+ createPlotMapForSingleDay(date),
76
+ [] // children
77
+ )
78
+ }
79
+
80
+ /**
81
+ * プロジェクト生成
82
+ */
83
+ function createSampleProject(name: string, tasks: TaskNode[]): Project {
84
+ const baseDate = new Date('2025-08-05') // 3日目(中間地点)
85
+ const startDate = new Date('2025-08-01')
86
+ const endDate = new Date('2025-08-07')
87
+
88
+ return new Project(tasks, baseDate, [], startDate, endDate, name)
89
+ }
90
+
91
+ // ============================================
92
+ // 3つのサンプルプロジェクト
93
+ // ============================================
94
+
95
+ // 日付定義(5稼働日: 8/1金, 8/4月, 8/5火, 8/6水, 8/7木)
96
+ const day1 = new Date('2025-08-01') // 金
97
+ const day2 = new Date('2025-08-04') // 月
98
+ const day3 = new Date('2025-08-05') // 火(基準日)
99
+ const day4 = new Date('2025-08-06') // 水
100
+ const day5 = new Date('2025-08-07') // 木
101
+
102
+ /**
103
+ * ケース1: 順調プロジェクト
104
+ * - タスク1〜3: 完了(100%)
105
+ * - タスク4〜5: 未着手(0%)
106
+ */
107
+ const onTrackTasks = [
108
+ createTask(1, 'タスク1', day1, 1.0),
109
+ createTask(2, 'タスク2', day2, 1.0),
110
+ createTask(3, 'タスク3', day3, 1.0),
111
+ createTask(4, 'タスク4', day4, 0),
112
+ createTask(5, 'タスク5', day5, 0),
113
+ ]
114
+ const onTrackProject = createSampleProject('順調', onTrackTasks)
115
+
116
+ /**
117
+ * ケース2: 遅延プロジェクト
118
+ * - タスク1: 完了(100%)
119
+ * - タスク2: 遅れ(50%)
120
+ * - タスク3〜5: 未着手(0%)
121
+ */
122
+ const delayedTasks = [
123
+ createTask(1, 'タスク1', day1, 1.0),
124
+ createTask(2, 'タスク2', day2, 0.5),
125
+ createTask(3, 'タスク3', day3, 0),
126
+ createTask(4, 'タスク4', day4, 0),
127
+ createTask(5, 'タスク5', day5, 0),
128
+ ]
129
+ const delayedProject = createSampleProject('遅延', delayedTasks)
130
+
131
+ /**
132
+ * ケース3: 失速プロジェクト
133
+ * - タスク1〜2: 完了(100%)
134
+ * - タスク3: 急に失速(20%)
135
+ * - タスク4〜5: 未着手(0%)
136
+ */
137
+ const stalledTasks = [
138
+ createTask(1, 'タスク1', day1, 1.0),
139
+ createTask(2, 'タスク2', day2, 1.0),
140
+ createTask(3, 'タスク3', day3, 0.2),
141
+ createTask(4, 'タスク4', day4, 0),
142
+ createTask(5, 'タスク5', day5, 0),
143
+ ]
144
+ const stalledProject = createSampleProject('失速', stalledTasks)
145
+
146
+ // ============================================
147
+ // 出力
148
+ // ============================================
149
+
150
+ function printProjectStats(project: Project) {
151
+ const stats = project.statisticsByProject[0]
152
+ const bac = stats.totalWorkloadExcel ?? 0
153
+ const pv = stats.totalPvCalculated ?? 0
154
+ const ev = stats.totalEv ?? 0
155
+ const spi = stats.spi ?? 0
156
+ const etcPrime = stats.etcPrime
157
+ const completionForecast = stats.completionForecast
158
+
159
+ console.log(`\n### ${stats.projectName}`)
160
+ console.log(`| 指標 | 値 | 説明 |`)
161
+ console.log(`|-----|-----|------|`)
162
+ console.log(`| BAC | ${bac}人日 | 総予定工数 |`)
163
+ console.log(`| PV | ${pv}人日 | 3日目までの計画 |`)
164
+ console.log(`| EV | ${ev}人日 | 実績 |`)
165
+ console.log(`| SPI | ${spi.toFixed(3)} | EV / PV |`)
166
+ if (etcPrime !== undefined) {
167
+ console.log(`| ETC' | ${etcPrime.toFixed(2)}人日 | (BAC - EV) / SPI |`)
168
+ }
169
+ if (completionForecast) {
170
+ console.log(
171
+ `| 完了予測日 | ${completionForecast.toISOString().split('T')[0]} | |`
172
+ )
173
+ }
174
+
175
+ // 終了予定日との比較
176
+ const endDate = new Date(stats.endDate)
177
+ if (completionForecast) {
178
+ const delayMs = completionForecast.getTime() - endDate.getTime()
179
+ const delayDays = Math.round(delayMs / (1000 * 60 * 60 * 24))
180
+ console.log(`| 遅延日数 | ${delayDays}日 | |`)
181
+ }
182
+ }
183
+
184
+ function printSummaryTable(projects: Project[]) {
185
+ console.log(`\n## サマリ比較`)
186
+ console.log(
187
+ `| プロジェクト | BAC | PV | EV | SPI | 完了予測日 | 遅延日数 |`
188
+ )
189
+ console.log(`|-------------|-----|-----|-----|-----|-----------|---------|`)
190
+
191
+ for (const project of projects) {
192
+ const stats = project.statisticsByProject[0]
193
+ const bac = stats.totalWorkloadExcel ?? 0
194
+ const pv = stats.totalPvCalculated ?? 0
195
+ const ev = stats.totalEv ?? 0
196
+ const spi = stats.spi ?? 0
197
+ const completionForecast = stats.completionForecast
198
+ const endDate = new Date(stats.endDate)
199
+
200
+ let forecastStr = '-'
201
+ let delayStr = '-'
202
+ if (completionForecast) {
203
+ forecastStr = completionForecast.toISOString().split('T')[0]
204
+ const delayMs = completionForecast.getTime() - endDate.getTime()
205
+ const delayDays = Math.round(delayMs / (1000 * 60 * 60 * 24))
206
+ delayStr = `${delayDays}日`
207
+ }
208
+
209
+ console.log(
210
+ `| ${stats.projectName} | ${bac}人日 | ${pv}人日 | ${ev}人日 | ${spi.toFixed(3)} | ${forecastStr} | ${delayStr} |`
211
+ )
212
+ }
213
+ }
214
+
215
+ // メイン実行
216
+ console.log('# EVM指標サンプル出力')
217
+ console.log('')
218
+ console.log('基準日: 2025-08-05(3日目)')
219
+ console.log('期間: 2025-08-01 〜 2025-08-07(5稼働日)')
220
+
221
+ printProjectStats(onTrackProject)
222
+ printProjectStats(delayedProject)
223
+ printProjectStats(stalledProject)
224
+
225
+ printSummaryTable([onTrackProject, delayedProject, stalledProject])
226
+
227
+ // 累積SPIの落とし穴を示す
228
+ console.log(`\n## 累積SPIの落とし穴`)
229
+ console.log('')
230
+ console.log('失速プロジェクトの直近1日(タスク3)のSPIを計算:')
231
+ const task3Stalled = stalledTasks[2]
232
+ console.log(`- タスク3のPV: ${task3Stalled.pv}`)
233
+ console.log(`- タスク3のEV: ${task3Stalled.ev}`)
234
+ console.log(`- タスク3のSPI: ${task3Stalled.spi?.toFixed(3)} ← 深刻!`)
235
+ console.log('')
236
+ console.log('直近SPIで完了予測を再計算すると:')
237
+ const recentSpi = task3Stalled.spi ?? 0.2
238
+ const bac = 5
239
+ const ev = 2.2
240
+ const etcPrimeRecent = (bac - ev) / recentSpi
241
+ console.log(
242
+ `- ETC' = (${bac} - ${ev}) / ${recentSpi} = ${etcPrimeRecent.toFixed(1)}人日`
243
+ )
244
+ console.log(`- 完了予測日 = 2025-08-05 + ${Math.ceil(etcPrimeRecent)}稼働日 ≒ 2025-08-25`)
245
+ console.log(`- 遅延日数 = 18日(累積SPI版の4.5倍!)`)