Skip to content

Commit b98b741

Browse files
Adding minimum time plugin.
Added pluginOrder to sort plugin execution order. Add Minimum plugin. Add Round plugin. Add Ceiling plugin. Improvement might be to add timeModified description of all plugins with previous duration and new duration into the summary.
1 parent a5ac2bf commit b98b741

File tree

7 files changed

+153
-6
lines changed

7 files changed

+153
-6
lines changed

README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,63 @@ More Verbose Mode
107107
node index --verbose=5
108108
```
109109

110+
## Plugins
111+
112+
Plugins can be loaded in.
113+
114+
Optional argument of pluginOrder lets you sort the order in which plugins are loaded.
115+
116+
Example Config:
117+
```json
118+
{
119+
"plugins": {
120+
"CollateTimeEntries": { "pluginOrder": 1, "enabled": true, "keyTags": [ "Overtime" ] },
121+
"CompleteTicketViaTag": { "pluginOrder": 2, "enabled": true },
122+
"StoreSummaryAsCommentOnTicketViaTag": { "pluginOrder": 3, "enabled": true },
123+
"SyncResultsToHTML": { "pluginOrder": 4, "enabled": true },
124+
"Minimum" : { "pluginOrder": 10, "minutes": 15 }
125+
}
126+
}
127+
```
128+
129+
### Collate Time Entries
130+
131+
Collates time entries into a single record if they are the same project, issue number, comment, billable status, day.
132+
133+
### Complete Ticket Via Tag
134+
135+
Completes a ticket in Active Collab based on the Tag given.
136+
137+
### Minimum
138+
139+
Lets you have a minimum time for each entry.
140+
15 minutes default.
141+
142+
Arguments:
143+
duration: seconds
144+
145+
### Round
146+
147+
Lets you round a time entry duration to a chuck of seconds.
148+
149+
Arguments:
150+
duration: seconds
151+
152+
### Ceiling
153+
154+
Lets you round up a time entry duration to a chuck of seconds.
155+
156+
Arguments:
157+
duration: seconds
158+
159+
160+
### Store Summary As Comment On Ticket Via Tag
161+
162+
Store the summary from the time entry onto the Active Collab ticket if the tag is set.
163+
164+
### Sync Results To HTML
165+
Recommended, gives an HTML report after syncing that can be used to see potential issues.
166+
110167
## License etc...
111168
License: MIT
112169

config/config.dist.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@
2121
"CompaniesAndProjects": "0 * * * *",
2222
"TimeEntries": "*/5 * * * *"
2323
},
24-
"projectTasksCacheTTLSeconds": 300
24+
"projectTasksCacheTTLSeconds": 300,
25+
"plugins": {}
2526
}

index.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ async function Main () {
5757
const plugins = []
5858
async function LoadPlugins () {
5959
if (config.plugins) {
60-
Object.entries(config.plugins).forEach(
60+
Object.entries(config.plugins).sort((a, b) => {
61+
return (a[1].pluginOrder || 0) - (b[1].pluginOrder || 0)
62+
}).forEach(
6163
([pluginName, pluginConfig]) => {
6264
var scriptName = pluginName
6365
pluginConfig.name = pluginName
@@ -147,7 +149,7 @@ async function ConfigCheck () {
147149
// people/PeopleId/users
148150

149151
throw new Error(
150-
"Expecting config.togglToActiveCollabUserMapping to map the Toggl User ID's to Active Collab User ID's.\nExample: { \"4118110\": 315 }"
152+
"Expecting config.togglToActiveCollabUserMapping to map the Toggl User ID's to Active Collab User ID's.\nExample: { \"2108110\": 925 }"
151153
)
152154
}
153155

@@ -786,7 +788,8 @@ async function SyncTimeEntries () {
786788
let timeEntry = timeEntries[index]
787789

788790
if (timeEntry.wid !== config.Toggl.workspaceId) {
789-
await deletePreviousTimeEntryMapping()
791+
// TODO: Fix error Cannot access 'deletePreviousTimeEntryMapping' before initialization
792+
//await deletePreviousTimeEntryMapping()
790793
syncResults.ignored.push({
791794
summary: 'Not in workspace.',
792795
timeEntry: timeEntry
@@ -803,7 +806,8 @@ async function SyncTimeEntries () {
803806
var activeCollabUserId =
804807
config.togglToActiveCollabUserMapping[timeEntry.uid]
805808
if (activeCollabUserId === undefined) {
806-
await deletePreviousTimeEntryMapping()
809+
// TODO: Fix error Cannot access 'deletePreviousTimeEntryMapping' before initialization
810+
// await deletePreviousTimeEntryMapping()
807811
syncResults.failed.push({
808812
summary: `User ID mapping not found for Time Entry UID: ${
809813
timeEntry.uid
@@ -829,6 +833,10 @@ async function SyncTimeEntries () {
829833

830834
let previousTimeEntryMapping = timeMappings.getRecord(timeEntry.id)
831835

836+
if (previousTimeEntryMapping !== null) {
837+
await eventEmitter.emit('beforeDeletePreviousTimeEntry', { timeEntry: timeEntry, previousTimeEntryMapping: previousTimeEntryMapping, syncResults: syncResults })
838+
}
839+
832840
let deletePreviousTimeEntryMapping = async (deleteMappingRecord = true) => {
833841
if (previousTimeEntryMapping === null || previousTimeEntryMapping === undefined) {
834842
return

plugins/Ceiling.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const events = require('../MyEventEmitter')
2+
const activeCollab = require('../MyActiveCollabClient')
3+
4+
module.exports = {
5+
init: function (pluginConfig) {
6+
7+
// Default duration to round of 5 minutes.
8+
if (typeof pluginConfig.duration === 'undefined') {
9+
pluginConfig.duration = 300
10+
}
11+
12+
events.on('onTimeEntry', async function (event) {
13+
let timeEntry = event.timeEntry
14+
15+
// Don't set the minimum duration if skipped or collated.
16+
if (timeEntry.skip || timeEntry.collated) {
17+
return
18+
}
19+
20+
let roundDuration = Math.ceil(timeEntry.duration / pluginConfig.duration) * pluginConfig.duration
21+
22+
if (roundDuration != timeEntry.duration) {
23+
timeEntry.timeModified = 'ceil'
24+
timeEntry.duration = roundDuration
25+
}
26+
})
27+
}
28+
}

plugins/Minimum.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const events = require('../MyEventEmitter')
2+
const activeCollab = require('../MyActiveCollabClient')
3+
4+
module.exports = {
5+
init: function (pluginConfig) {
6+
7+
if (typeof pluginConfig.duration === 'undefined') {
8+
pluginConfig.duration = 15 * 60
9+
}
10+
11+
events.on('onTimeEntry', async function (event) {
12+
let timeEntry = event.timeEntry
13+
14+
// Don't set the minimum duration if skipped or collated.
15+
if (timeEntry.skip || timeEntry.collated) {
16+
return
17+
}
18+
19+
if (timeEntry.duration < pluginConfig.duration) {
20+
timeEntry.timeModified = 'minimum ' + pluginConfig.duration
21+
timeEntry.duration = pluginConfig.duration
22+
}
23+
})
24+
}
25+
}

plugins/Round.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const events = require('../MyEventEmitter')
2+
const activeCollab = require('../MyActiveCollabClient')
3+
4+
module.exports = {
5+
init: function (pluginConfig) {
6+
7+
// Default duration to round of 5 minutes.
8+
if (typeof pluginConfig.duration === 'undefined') {
9+
pluginConfig.duration = 300
10+
}
11+
12+
events.on('onTimeEntry', async function (event) {
13+
let timeEntry = event.timeEntry
14+
15+
// Don't set the minimum duration if skipped or collated.
16+
if (timeEntry.skip || timeEntry.collated) {
17+
return
18+
}
19+
20+
let roundDuration = Math.round(timeEntry.duration / pluginConfig.duration) * pluginConfig.duration
21+
22+
if (roundDuration != timeEntry.duration) {
23+
timeEntry.timeModified = 'round'
24+
timeEntry.duration = roundDuration
25+
}
26+
})
27+
}
28+
}

plugins/SyncResultsToHTML.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ Recent: ${syncResults.recent.length}<br>
241241
? '#' + result.timeEntry.issueNumber
242242
: ''
243243
}</td>`
244-
output += `<td>${result.timeEntry.billable ? '<small>$ Billable</small><br>' : ''}${result.timeEntry.tags !== undefined && result.timeEntry.tags.length > 0 ? "<small><span>"+result.timeEntry.tags.join('</span><span>')+"</span></small><br>" : ''}${result.timeEntry.collatedTimeEntries !== undefined ? '<small>Collated ('+result.timeEntry.collatedTimeEntries.length+')</small>' : ''}<xmp>${result.timeEntry.summary || result.timeEntry.description || ''}</xmp></td>`
244+
output += `<td>${result.timeEntry.billable ? '<small>$ Billable</small><br>' : ''}${result.timeEntry.tags !== undefined && result.timeEntry.tags.length > 0 ? "<small><span>"+result.timeEntry.tags.join('</span><span>')+"</span></small><br>" : ''}${result.timeEntry.collatedTimeEntries !== undefined ? '<small>Collated ('+result.timeEntry.collatedTimeEntries.length+')</small>' : ''}<xmp>${result.timeEntry.summary || result.timeEntry.description || ''}</xmp>${result.timeEntry.timeModified ? '<br><small>Time was modified ' + result.timeEntry.timeModified + '</small>': '' }</td>`
245245
output += `<td>${moment.utc(durationSeconds*1000).format('HH:mm:ss')}</td>
246246
<td>
247247
`

0 commit comments

Comments
 (0)