mirror of
https://github.com/Ellpeck/ObsidianSimpleTimeTracker.git
synced 2024-11-16 07:23:12 +01:00
added the ability to copy the table as markdown or csv
This commit is contained in:
parent
10251de71c
commit
90c6b55ad7
9 changed files with 191 additions and 51 deletions
|
@ -17,7 +17,6 @@ The tracker's information is stored in the code block as JSON data. The names, s
|
|||
Super Simple Time Tracker is still in its early stages! There are a lot of plans for it, including:
|
||||
- A setting to link segments to corresponding daily notes automatically
|
||||
- A neat interface to edit previous segments' names and time stamps
|
||||
- The ability to copy the table in various formats, including as text, markdown, and csv
|
||||
- A fancier Start and End button
|
||||
|
||||
# 🙏 Acknowledgements
|
||||
|
|
|
@ -20,7 +20,7 @@ export class SimpleTimeTrackerSettingsTab extends PluginSettingTab {
|
|||
.setDesc(createFragment(f => {
|
||||
f.createSpan({ text: "The way that timestamps in time tracker tables should be displayed. Uses " });
|
||||
f.createEl("a", { text: "moment.js", href: "https://momentjs.com/docs/#/parsing/string-format/" });
|
||||
f.createSpan({ text: " syntax. Clear to reset to default." });
|
||||
f.createSpan({ text: " syntax." });
|
||||
}))
|
||||
.addText(t => {
|
||||
t.setValue(String(this.plugin.settings.timestampFormat));
|
||||
|
@ -30,6 +30,17 @@ export class SimpleTimeTrackerSettingsTab extends PluginSettingTab {
|
|||
});
|
||||
});
|
||||
|
||||
new Setting(this.containerEl)
|
||||
.setName("CSV Delimiter")
|
||||
.setDesc("The delimiter character that should be used when copying a tracker table as CSV. For example, some languages use a semicolon instead of a comma.")
|
||||
.addText(t => {
|
||||
t.setValue(String(this.plugin.settings.csvDelimiter));
|
||||
t.onChange(async v => {
|
||||
this.plugin.settings.csvDelimiter = v.length ? v : defaultSettings.csvDelimiter;
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
this.containerEl.createEl("hr");
|
||||
this.containerEl.createEl("p", { text: "If you like this plugin and want to support its development, you can do so through my website by clicking this fancy image!" });
|
||||
this.containerEl.createEl("a", { href: "https://ellpeck.de/support" })
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
export const defaultSettings: SimpleTimeTrackerSettings = {
|
||||
timestampFormat: "YY-MM-DD hh:mm:ss"
|
||||
timestampFormat: "YY-MM-DD hh:mm:ss",
|
||||
csvDelimiter: ","
|
||||
};
|
||||
|
||||
export interface SimpleTimeTrackerSettings {
|
||||
|
||||
timestampFormat: string;
|
||||
csvDelimiter: string;
|
||||
|
||||
}
|
||||
|
|
|
@ -81,27 +81,36 @@ export function displayTracker(tracker: Tracker, element: HTMLElement, getSectio
|
|||
let total = totalDiv.createEl("span", { cls: "simple-time-tracker-timer-time", text: "0s" });
|
||||
totalDiv.createEl("span", { text: "Total" });
|
||||
|
||||
// add table
|
||||
if (tracker.entries.length > 0) {
|
||||
// add table
|
||||
let table = element.createEl("table", { cls: "simple-time-tracker-table" });
|
||||
table.createEl("tr").append(
|
||||
createEl("th", { text: "Segment" }),
|
||||
createEl("th", { text: "Start time" }),
|
||||
createEl("th", { text: "End time" }),
|
||||
createEl("th", { text: "Total" }));
|
||||
createEl("th", { text: "Duration" }));
|
||||
|
||||
for (let entry of tracker.entries) {
|
||||
let row = table.createEl("tr");
|
||||
row.createEl("td", { text: entry.name });
|
||||
row.createEl("td", { text: moment.unix(entry.startTime).format(settings.timestampFormat) });
|
||||
row.createEl("td", { text: formatTimestamp(entry.startTime, settings) });
|
||||
if (entry.endTime) {
|
||||
row.createEl("td", { text: moment.unix(entry.endTime).format(settings.timestampFormat) });
|
||||
let duration = moment.unix(entry.endTime).diff(moment.unix(entry.startTime));
|
||||
row.createEl("td", { text: getCountdownDisplay(moment.duration(duration)) });
|
||||
row.createEl("td", { text: formatTimestamp(entry.endTime, settings) });
|
||||
row.createEl("td", { text: formatDurationBetween(entry.startTime, entry.endTime) });
|
||||
}
|
||||
}
|
||||
|
||||
// add copy buttons
|
||||
let buttons = element.createEl("div", { cls: "simple-time-tracker-bottom" });
|
||||
new ButtonComponent(buttons)
|
||||
.setButtonText("Copy as table")
|
||||
.onClick(() => navigator.clipboard.writeText(createMarkdownTable(tracker, settings)));
|
||||
new ButtonComponent(buttons)
|
||||
.setButtonText("Copy as csv")
|
||||
.onClick(() => navigator.clipboard.writeText(createCsv(tracker, settings)));
|
||||
}
|
||||
|
||||
|
||||
setCountdownValues(tracker, current, total, currentDiv);
|
||||
let intervalId = window.setInterval(() => {
|
||||
// we delete the interval timer when the element is removed
|
||||
|
@ -113,7 +122,35 @@ export function displayTracker(tracker: Tracker, element: HTMLElement, getSectio
|
|||
}, 1000);
|
||||
}
|
||||
|
||||
function getCountdownDisplay(duration: moment.Duration): string {
|
||||
function setCountdownValues(tracker: Tracker, current: HTMLElement, total: HTMLElement, currentDiv: HTMLDivElement) {
|
||||
let currEntry = tracker.entries.last();
|
||||
if (currEntry) {
|
||||
if (!currEntry.endTime)
|
||||
current.setText(formatDurationBetween(currEntry.startTime, moment().unix()));
|
||||
total.setText(formatDuration(getTotalDuration(tracker)));
|
||||
}
|
||||
currentDiv.hidden = !currEntry || !!currEntry.endTime;
|
||||
}
|
||||
|
||||
function getTotalDuration(tracker: Tracker): number {
|
||||
let totalDuration = 0;
|
||||
for (let entry of tracker.entries) {
|
||||
let endTime = entry.endTime ? moment.unix(entry.endTime) : moment();
|
||||
totalDuration += endTime.diff(moment.unix(entry.startTime));
|
||||
}
|
||||
return totalDuration;
|
||||
}
|
||||
|
||||
function formatTimestamp(timestamp: number, settings: SimpleTimeTrackerSettings): string {
|
||||
return moment.unix(timestamp).format(settings.timestampFormat);
|
||||
}
|
||||
|
||||
function formatDurationBetween(startTime: number, endTime: number): string {
|
||||
return formatDuration(moment.unix(endTime).diff(moment.unix(startTime)));
|
||||
}
|
||||
|
||||
function formatDuration(totalTime: number): string {
|
||||
let duration = moment.duration(totalTime);
|
||||
let ret = "";
|
||||
if (duration.hours() > 0)
|
||||
ret += duration.hours() + "h ";
|
||||
|
@ -123,20 +160,39 @@ function getCountdownDisplay(duration: moment.Duration): string {
|
|||
return ret;
|
||||
}
|
||||
|
||||
function setCountdownValues(tracker: Tracker, current: HTMLElement, total: HTMLElement, currentDiv: HTMLDivElement) {
|
||||
let currEntry = tracker.entries.last();
|
||||
if (currEntry) {
|
||||
if (!currEntry.endTime) {
|
||||
let currDuration = moment().diff(moment.unix(currEntry.startTime));
|
||||
current.setText(getCountdownDisplay(moment.duration(currDuration)));
|
||||
function createMarkdownTable(tracker: Tracker, settings: SimpleTimeTrackerSettings): string {
|
||||
let table = [["Segment", "Start time", "End time", "Duration"]];
|
||||
for (let entry of tracker.entries)
|
||||
table.push(createTableRow(entry, settings));
|
||||
table.push(["**Total**", "", "", `**${formatDuration(getTotalDuration(tracker))}**`]);
|
||||
|
||||
let ret = "";
|
||||
// calculate the width every column needs to look neat when monospaced
|
||||
let widths = Array.from(Array(4).keys()).map(i => Math.max(...table.map(a => a[i].length)));
|
||||
for (let r = 0; r < table.length; r++) {
|
||||
// add separators after first row
|
||||
if (r == 1)
|
||||
ret += Array.from(Array(4).keys()).map(i => "-".repeat(widths[i])).join(" | ") + "\n";
|
||||
|
||||
let row: string[] = [];
|
||||
for (let i = 0; i < 4; i++)
|
||||
row.push(table[r][i].padEnd(widths[i], " "));
|
||||
ret += row.join(" | ") + "\n";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
let totalDuration = 0;
|
||||
for (let entry of tracker.entries) {
|
||||
let endTime = entry.endTime ? moment.unix(entry.endTime) : moment();
|
||||
totalDuration += endTime.diff(moment.unix(entry.startTime));
|
||||
function createCsv(tracker: Tracker, settings: SimpleTimeTrackerSettings): string {
|
||||
let ret = "";
|
||||
for (let entry of tracker.entries)
|
||||
ret += createTableRow(entry, settings).join(settings.csvSeparator) + "\n";
|
||||
return ret;
|
||||
}
|
||||
total.setText(getCountdownDisplay(moment.duration(totalDuration)));
|
||||
}
|
||||
currentDiv.hidden = !currEntry || !!currEntry.endTime;
|
||||
|
||||
function createTableRow(entry: Entry, settings: SimpleTimeTrackerSettings): string[] {
|
||||
return [
|
||||
entry.name,
|
||||
formatTimestamp(entry.startTime, settings),
|
||||
entry.endTime ? formatTimestamp(entry.endTime, settings) : "",
|
||||
entry.endTime ? formatDurationBetween(entry.startTime, entry.endTime) : ""];
|
||||
}
|
||||
|
|
|
@ -20,7 +20,12 @@
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.simple-time-tracker-timers {
|
||||
.simple-time-tracker-bottom button {
|
||||
margin: 10px 5px 10px 5px;
|
||||
}
|
||||
|
||||
.simple-time-tracker-timers,
|
||||
.simple-time-tracker-bottom {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
"timestampFormat": "YY-MM-DD hh:mm:ss"
|
||||
"timestampFormat": "YY-MM-DD hh:mm:ss",
|
||||
"csvSeparator": ","
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -20,7 +20,12 @@
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.simple-time-tracker-timers {
|
||||
.simple-time-tracker-bottom button {
|
||||
margin: 10px 5px 10px 5px;
|
||||
}
|
||||
|
||||
.simple-time-tracker-timers,
|
||||
.simple-time-tracker-bottom {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
|
|
|
@ -2,5 +2,17 @@
|
|||
These are the notes for my cool project. There's so much left to do! I wish I had a way to track the amount of time I spend on each part of the project.
|
||||
|
||||
```simple-time-tracker
|
||||
{"entries":[{"name":"Think about project","startTime":1664305777,"endTime":1664308788},{"name":"Create project note","startTime":1664308810,"endTime":1664308815},{"name":"Work on project","startTime":1664308830,"endTime":1664309301}]}
|
||||
{"entries":[{"name":"Think about project","startTime":1664305777,"endTime":1664308788},{"name":"Create project note","startTime":1664308810,"endTime":1664308815},{"name":"Work on project","startTime":1664308830,"endTime":1664309301},{"name":"Segment 4","startTime":1664364444,"endTime":1664364449},{"name":"Segment 5","startTime":1664364495,"endTime":1664364498}]}
|
||||
```
|
||||
|
||||
```
|
||||
Think about project;22-09-27 09:09:37;22-09-27 09:59:48;50m 11s
|
||||
Create project note;22-09-27 10:00:10;22-09-27 10:00:15;5s
|
||||
Work on project;22-09-27 10:00:30;22-09-27 10:08:21;7m 51s
|
||||
Segment 4;22-09-28 01:27:24;22-09-28 01:27:29;5s
|
||||
Segment 5;22-09-28 01:28:15;22-09-28 01:28:18;3s
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue