Skip to content

Commit

Permalink
add group by feature
Browse files Browse the repository at this point in the history
  • Loading branch information
hidetak committed Mar 8, 2020
1 parent 4bf141c commit 0cb03d4
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 36 deletions.
36 changes: 22 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,28 +82,36 @@ package.jsonをテキストエディタで開くと以下のような記載を
3: hoge2
select board number: 2
```

Trelloからのデータのダウンロードが開始します。
しばらく待つと次に以下が表示されます。

```
0: Show only header
1: All
2: Specify condition by js
Select output data:
1: set group by
2: input filter and show data
current group by:
select:
```

出力したいデータの種類を左側の番号で選択します。
実施したいことを左側の番号で選択します。

1を入力すると、group byを設定できます。
group byは集計する際にまとめる変数であり、例えば`member`と入力するとメンバーの種類毎にPointの合計と、timeの合計がcsvで出力されるようになります。
group byの指定の際に何も表示せずにリターンすると、group byが未設定となります。
```
select: 1
specify variable name, empty string means no group by
variable names: cardId,number,title,point,listName,inDate,outDate,time,labelPink,labelGreen,member
group by: member
```

0を入力すると、CSVのヘッダのみを標準出力に出力します。
1を入力すると、全データをCSV形式で標準出力に出力します。
2を入力すると、JavaScriptの条件文により出力するデータを選択できるようになります。
2を入力すると、JavaScriptの条件文を入力できます。
条件文を入力すると、各行をその条件文で評価し、trueとなった行のみを出力・集計の対象とします。

```
0: Show only header
1: All
2: Specify condition by js
Select output data: 2
input condition by js: listName === "Doing"
select: 2
specify filter by javascript condition, the following variables are available, "true" means showing all data
variable names: cardId,number,title,point,listName,inDate,outDate,time,labelPink,labelGreen,member
input condition: inDate > new Date("2020/2/22") && listName === "Doing"
```

条件文の例を示します。
Expand Down
126 changes: 105 additions & 21 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const readLine = require('readline')
const fetch = require('node-fetch')
const moment = require('moment-timezone')
const Iconv = require('iconv').Iconv
const clc = require('cli-color')

if (process.argv.length !== 5) {
console.error(
Expand Down Expand Up @@ -31,6 +32,11 @@ const isLinux = process.platform === 'linux'
// 文字コード変換の準備
const iconv = new Iconv('UTF-8', 'SHIFT_JIS//IGNORE')

// color設定
const inf = clc.xterm(83)
const st = clc.xterm(50)
const er = clc.xterm(204)

const readUserInput = (question, initialInput) => {
const rl = readLine.createInterface({
input: process.stdin,
Expand All @@ -51,10 +57,10 @@ const readUserInput = (question, initialInput) => {
const inputText = async (questionText, re, initialInput) => {
while (true) {
let a = await readUserInput(questionText, initialInput)
if (a && a.match(re)) {
if (a.match(re)) {
return a
} else {
console.log('error: invalid input')
console.log(er('error: invalid input'))
}
}
}
Expand Down Expand Up @@ -157,7 +163,7 @@ const parseData = (actionMap, cardsMap) => {
return list
}

const writeList = list => {
const writeList = (list, showiingData) => {
console.log(
'cardId,number,title,point,listName,inDate,outDate,time,labelPink,labelGreen,member'
)
Expand All @@ -176,6 +182,40 @@ const writeList = list => {
console.log(`total point: ${totalPoint}`)
}

const writeListGroup = (list, groupby) => {
console.log(`${groupby},totalPoint,totalTime`)
let total = 0
let timesByGroup = {}
let relatedCardIds = {}
let pointsByGroup = {}
let cardPointMap = {}
for (let d of list) {
total += new Date(d.outDate).getTime() - new Date(d.inDate).getTime()
cardPointMap[d.cardId] = d.point
timesByGroup[d[groupby]] = timesByGroup[d[groupby]]
? timesByGroup[d[groupby]] + d.time
: d.time
if (!relatedCardIds[d[groupby]] || !relatedCardIds[d[groupby]][d.cardId]) {
pointsByGroup[d[groupby]] = pointsByGroup[d[groupby]]
? pointsByGroup[d[groupby]] + d.point
: d.point
if (!relatedCardIds[d[groupby]]) {
relatedCardIds[d[groupby]] = {}
}
relatedCardIds[d[groupby]][d.cardId] = d.cardId
}
}
let totalPoint = 0
for (let k in cardPointMap) {
totalPoint += cardPointMap[k]
}
for (let k in timesByGroup) {
console.log(`"${k}","${pointsByGroup[k]}","${timesByGroup[k]}"`)
}
console.log(`total: ${total / 1000 / 60 / 60} hrs`)
console.log(`total point: ${totalPoint}`)
}

const main = async () => {
try {
// Board一覧取得
Expand All @@ -184,10 +224,10 @@ const main = async () => {
// Board一覧表示
let num = 1
for (const item of boardList) {
console.log(`${num++}: ${item.name}`)
console.log(inf(`${num++}: ${item.name}`))
}
// Board選択
let i = await inputText('select board number: ', `^[1-${num - 1}]$`)
let i = await inputText('select board by number: ', `^[1-${num - 1}]$`)
const selectedBoard = boardList[Number(i - 1)]
// User取得
let memberMap = {}
Expand Down Expand Up @@ -239,30 +279,70 @@ const main = async () => {
let list = parseData(actionMap, cardsMap)
// 出力内容選択
let initialCondition = ''
let groupby = ''
let showingList = [
'cardId',
'number',
'title',
'point',
'listName',
'inDate',
'outDate',
'time',
'labelPink',
'labelGreen',
'member'
]
while (true) {
console.log('----------')
console.log('0: Show only header')
console.log('1: All')
console.log('2: Specify condition by js')
const type = await inputText('Select output data: ', `^[0-2]$`)
if (type === '0') {
console.log('----------')
console.log(inf('----------'))
console.log(inf('1: set group by'))
console.log(inf('2: input filter and show data'))
console.log(st(`current group by: ${groupby}`))
const type = await inputText('select: ', `^[1-2]$`)
if (type === '1') {
console.log(
'cardId,number,title,point,listName,inDate,outDate,time,labelPink,labelGreen,member'
inf('specify variable name, empty string means no group by')
)
} else if (type === '1') {
console.log('----------')
writeList(list)
console.log(inf(` variable names: ${showingList.toString()}`))
groupbyText = await inputText('group by: ', `.*`, groupby)
try {
const cardId = 'cardId',
number = 'number',
title = 'title',
point = 'point',
listName = 'listName',
inDate = 'inDate',
outDate = 'outDate',
time = 'time',
labelPink = 'labelPink',
labelGreen = 'labelGreen',
member = 'member',
totalTime = 'totalTime'
if (groupbyText === '') {
groupbyText = '""'
}
groupby = eval(groupbyText)
} catch (err) {
console.error(er(err))
}
} else if (type === '2') {
console.log(
inf(
'specify filter by javascript condition, the following variables are available, "true" means showing all data'
)
)
console.log(inf(` variable names: ${showingList.toString()}`))
const condition = await inputText(
'input condition by js: ',
'input condition: ',
`.+`,
initialCondition
)
console.log('----------')
console.log(inf('----------'))
let newList = []
let total = 0
for (let d of list) {
const all = true
const header = false
let {
cardId,
number,
Expand All @@ -283,16 +363,20 @@ const main = async () => {
newList.push(d)
}
} catch (err) {
console.error(err)
console.error(er(err))
break
}
}
writeList(newList)
if (groupby !== '') {
writeListGroup(newList, groupby)
} else {
writeList(newList, showingList)
}
initialCondition = condition
}
}
} catch (err) {
console.error(err)
console.error(er(err))
}
}

Expand Down
Loading

0 comments on commit 0cb03d4

Please sign in to comment.