Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 022: Describe command #15

Merged
merged 7 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions .todo
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,35 @@ team:
email: [email protected]
name: Vladyslav Kurmaz
timeline:
"v0.2.0":
date: "2024-09-04"
"v0.3.0":
date: "2024-09-12"
"v0.4.0":
date: "2024-09-15"
"v0.5.0":
date: "2024-09-23"
"v0.6.0":
date: "2024-10-22"
"v0.7.0":
date: "2024-10"
- name: v0.7.0
date: 2024-10-28 23:55:00 GMT+0200
- name: v0.6.0
date: 2024-10-22 17:30:00 GMT+0200
- name: v0.5.0
date: 2024-09-23 17:30:00 GMT+0200
- name: v0.4.0
date: 2024-09-15 17:30:00 GMT+0200
- name: v0.3.0
date: 2024-09-12 17:30:00 GMT+0200
- name: v0.2.0
date: 2024-09-04 17:30:00 GMT+0200
tasks: |
[+:022:v0.7.0] Describe command - project @vlad.k
[+:021:v0.6.0] Add project description section @vlad.k
[+:020:v0.5.0] Add option to display tasks with different statuses: --backlog, --indev, --done @vlad.k
[+:019:v0.5.0] Add component as optional parameter for ls command @vlad.k
[-:018:v0.7.0] Generate snapshot @vlad.k
[-:018:v0.8.0] Generate snapshot @vlad.k
[+:017:v0.5.0] Add SRS section @vlad.k
[-:016:v0.7.0] Add describe command with resolved links @vlad.k
[-:016:v0.8.0] Add describe command with resolved links @vlad.k
[x:015:v0.5.0] Integrate .tpm folder at repository root level to store snapshots @vlad.k
[-:014:v0.7.0] Update README.md with Hello World section using <https://github.com/project-talan/tln-demo> repository @vlad.k
[-:014:v0.8.0] Update README.md with Hello World section using <https://github.com/project-talan/tln-demo> repository @vlad.k
[+:013:v0.6.0] Add search @vlad.k
[+:012:v0.6.0] Add CI/CD @vlad.k
[+:011:v0.6.0] Add unit test framework @vlad.k
[+:010:v0.6.0] Extract tags, timeline from task description @vlad.k
[+:009:v0.5.0] Add command to generate .todo template --team, --timeline, --tasks, --force @vlad.k
[+:008:v0.6.0] Merge multiple descriptions from one file @vlad.k
[-:007:v0.7.0] Server command: express server + fs watch @vlad.k
[-:007:v0.8.0] Server command: express server + fs watch @vlad.k
[-] Create API using express @vlad.k
[-] Implement web part: Dashboard @vlad.k
[-] Implement web part: Timeline @vlad.k
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Here is example for `.todo` file at repository root
| Symbol | Meaning |
| --- | --- |
| - | todo |
| > | in progress |
| > | in development |
| ? | to be discussed |
| ! | blocked |
| + | done |
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tln-pm",
"version": "0.6.0",
"version": "0.7.0",
"description": "Project Management as Code",
"main": "cli.js",
"scripts": {
Expand Down
5 changes: 3 additions & 2 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class App {
try {
if (!(who.all || aees.length)) {
aees = [].concat(exec(`git config --local --get user.email`, { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim());
}
}
} catch (e) {
this.logger.warn('Couldn\'t identify git user, please use -g <userid> option or --all option to define assignee(s)');
}
Expand All @@ -94,7 +94,8 @@ class App {
const c = this.rootComponent;
if (c) {
if (what.project) {
result.project = await c.describeProject();
result.projects = await c.describeProject();
// console.log(result.projects[0].summary.timeline);
}
}
return result;
Expand Down
57 changes: 52 additions & 5 deletions src/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

const path = require('path');
const fs = require('fs');
const assign = require('assign-deep');
const exec = require('child_process').execSync;

const assign = require('assign-deep');

const sourceFactory = require('./source');
const projectFactory = require('./project');
const memberFactory = require('./member');
const deadlineFactory = require('./deadline');
const timelineFactory = require('./timeline');
const taskFactory = require('./task');
const srsFactory = require('./srs');

Expand All @@ -23,10 +24,12 @@ class Component {
this.parent = parent;
this.home = home;
this.id = id;
this.checkRepo = true;
this.lastCommit = null;
this.sources = [];
this.project = [];
this.team = null;
this.timeline = null;
this.timeline = [];
this.tasks = [];
this.srs = null;
this.components = [];
Expand Down Expand Up @@ -62,6 +65,18 @@ class Component {

async process(pathTo) {
let result = false;
// check if component is git repo root
if (this.checkRepo) {
this.checkRepo = false;
if (fs.existsSync(path.join(this.home, '.git'))) {
try {
this.lastCommit = exec(`git --no-pager log -1 --pretty='format:%cd' --date='iso'`, { cwd: this.home, stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim();
} catch (e) {
this.logger.warn('Could\'n read git repository', this.home, e);
}
}
}
//
const items = pathTo.split(path.sep);
if (items.length > 1) {
// dive into subdirectories
Expand Down Expand Up @@ -102,8 +117,12 @@ class Component {
result |= true;
}
if (data.timeline) {
this.timeline = assign({}, data.timeline);
result |= true;
if (data.timeline.length) {
const timeline = timelineFactory.create(this.logger, source);
await timeline.load(data.timeline);
this.timeline.push(timeline);
result |= true;
}
}
if (data.tasks) {
const task = taskFactory.create(this.logger, source);
Expand Down Expand Up @@ -172,6 +191,8 @@ class Component {
}
// timeline
if (what.timeline && this.timeline) {
summary.timeline = summary.timeline.concat(await timeline.getSummary(0));

if (Object.keys(this.timeline).length) {
this.logger.con((require('yaml')).stringify({ timeline: this.timeline }));
}
Expand Down Expand Up @@ -237,13 +258,39 @@ class Component {
this.project.forEach( p => {
project = assign(project, p);
});
let summary = {
tasks: { todo: 0, indev: 0, tbd: 0, blocked: 0, done: 0, dropped: 0 },
timeline: []
};
project.summary = await this.getSummary(summary);
project.summary.team = this.getTeam({}, false, true);
project.summary.lastCommit = this.lastCommit;
projects.push(project);
}
const prs = await Promise.all(this.components.map(async c => c.describeProject()));
projects = projects.concat(...prs);
return projects;
}

async getSummary(summary) {
for (const timeline of this.timeline) {
summary.timeline = summary.timeline.concat(await timeline.getSummary({features: 0}));
}
for (const deadline of summary.timeline) {
const tsks = await Promise.all(this.tasks.map(async t => t.getCountByDeadlime(deadline.name)));
const cnt = tsks.reduce((acc, c) => acc + c, 0);
deadline.features += cnt;
}
//
for (const task of this.tasks) {
summary.tasks = await task.getSummary(summary.tasks);
};
for (const component of this.components) {
summary = await component.getSummary(summary);
}
return summary;
}

getTeam( team, up, down) {
let t = assign({}, team, this.team);
if (up && this.parent) {
Expand Down
17 changes: 0 additions & 17 deletions src/deadline.js

This file was deleted.

11 changes: 8 additions & 3 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const yaml = require('js-yaml');


const utils = require('./utils');
const {version} = require('../package.json');

class Server {

Expand Down Expand Up @@ -43,10 +44,14 @@ class Server {
ea.get('/main.js', (req, res) => {
res.send(getLocalContent('main.js'));
})
ea.get('/team', (req, res) => {
// API
ea.get('/info', (req, res) => {
res.send(this.makeResponce({version}));
})
ea.get('/teams', (req, res) => {
res.send(this.makeResponce(root.getTeam({}, true, true)));
})
ea.get('/dashboard', async(req, res) => {
ea.get('/projects', async(req, res) => {
res.send(this.makeResponce( await app.describe({ what: { project: true } })));
})

Expand All @@ -62,7 +67,7 @@ class Server {
})

ea.listen(port, () => {
this.logger.con(`start server on http://localhost:${port} in ${readOnly?'read-only':'read-write'} mode`);
this.logger.con(`start server version ${version} on http://localhost:${port} in ${readOnly?'read-only':'read-write'} mode`);
})

}
Expand Down
25 changes: 24 additions & 1 deletion src/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,30 @@ class Task {
};
}
}


async getSummary(tasksSummary) {
if (this.tasks.length) {
for (const task of this.tasks) {
tasksSummary = await task.getSummary(tasksSummary);
}
} else {
switch (this.status) {
case '-': tasksSummary.todo++; break;
case '>': tasksSummary.indev++; break;
case '?': tasksSummary.tbd++; break;
case '!': tasksSummary.blocked++; break;
case '+': tasksSummary.done++; break;
case 'x': tasksSummary.dropped++; break;
}
}
return tasksSummary;
}

async getCountByDeadlime(deadline) {
const st = await Promise.all(this.tasks.map(async t => t.getCountByDeadlime(deadline)));
return this.deadline === deadline ? 1 : 0 + st.reduce((acc, c) => acc + c, 0);
}

}

module.exports.create = (logger, source) => {
Expand Down
25 changes: 25 additions & 0 deletions src/timeline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict';

const assign = require('assign-deep');

class Timeline {

constructor(logger, source) {
this.logger = logger;
this.source = source;
this.deadlines = [];
}

async load(data) {
this.deadlines = data.map(r => assign({}, r));
}

async getSummary(options) {
return this.deadlines.map(r => { return { ...assign({}, r), ...options } });
}

}

module.exports.create = (logger, source) => {
return new Timeline(logger, source);
}
4 changes: 2 additions & 2 deletions src/deadline.spec.js → src/timeline.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const chai = require('chai');

const deadlineFactory = require('./deadline');
const timelineFactory = require('./timeline');

const { expect } = chai;

Expand All @@ -9,7 +9,7 @@ const logger = require('./logger').create(0);
describe('Timeline entity', function () {

it('can be created', function () {
expect(deadlineFactory.create(logger)).not.to.be.null;
expect(timelineFactory.create(logger)).not.to.be.null;
});

});
Loading