Skip to content

Commit

Permalink
Add Reporting Summary API and examples
Browse files Browse the repository at this point in the history
  • Loading branch information
serrnovik committed Nov 4, 2024
1 parent c478ce6 commit 40170e4
Show file tree
Hide file tree
Showing 9 changed files with 600 additions and 30 deletions.
90 changes: 90 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,96 @@ A time tracker is really just a special code block that stores information about

The tracker's information is stored in the code block as JSON data. The names, start times and end times of each segment are stored. They're displayed neatly in the code block in preview or reading mode.

# Tim Tracking Summary / Reporting

1. **Time Tracking Entries with Tags**: Track work sessions with tags to categorize different activities.

- Example of an entry: `#tt_dev #tt_client_a #tt_frontend` represents time spent working on the development (frontend) for a specific client.

2. **Enhanced Reporting Functionality**: Generate time tracking reports for specific time periods, allowing detailed insight into how time was allocated.

- **Stream-based Reports**: View summaries of time based on specific streams such as Development, Accounting, etc.

- **Client-based Reports**: Track hours spent working for specific clients.


![alt text](reporting-screenshot.png)

The output within Obsidian will render detailed information for each time segment, as shown in the first image.

## Example Report

Call command `Ctrl+P` select `Insert Time Tracking Summary`

The reporting capability allows generating summaries for specific time ranges and topics:

- **Streams Report**: A summary of all topics (e.g., Development, Accounting) over a selected period.

```
time-tracking-summary
"2024-11-01", "2024-11-30"
```
- **Clients Report**: A summary for individual topic over a given time range.
```
time-tracking-summary
"2024-11-01", "2024-11-30", clients
```
These examples help demonstrate how you can leverage the new tracking and reporting capabilities.
## How to Use
1. **Tag Configuration**
- Configure your tags, icons, and sub tags using YAML in the settings of the plugin.
- Example configuration can be found in the settings:
2. **Tag your records with one or more tags / sub tags**
3. **Inserting Time Tracking Summary**
- Use the newly added command to insert the time tracking summary snippet into a markdown file.
- This will generate a report for a given period, optionally filtered by a specific topic.
```yaml
# You can have as many 'sections' as you want to track different domains separately or in parallel
# Example secction / topic 1
streams:
name: "🌊 Streams"
items:
- topic: "Accounting"
icon: "🧮"
tag: "#tt_accounting"
subTags: []
- topic: "Development"
icon: "💗"
tag: "#tt_dev"
subTags:
- topic: "Frontend"
tag: "#tt_frontend"
subTags: []
- topic: "Backend"
tag: "#tt_backend"
subTags: []
# Example section / topic 2
clients:
name: "👨🏼‍💼 Clients"
items:
- topic: "Client A"
tag: "#tt_client_a"
subTags: []
- topic: "Client B"
tag: "#tt_client_b"
subTags: []`
```

# 🙏 Acknowledgements
If you like this plugin and want to support its development, you can do so through my website by clicking this fancy image!

Expand Down
Binary file added reporting-screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// interfaces.ts
export interface SubTag {
topic: string;
tag: string;
icon?: string;
subTags: SubTag[];
}

export interface Item extends SubTag {}

export interface Section {
name: string;
items: Item[];
}

export interface Configuration {
[sectionKey: string]: Section;
}
135 changes: 110 additions & 25 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@
import { MarkdownRenderChild, Plugin, TFile } from "obsidian";
import { Editor, MarkdownRenderChild, moment, Plugin, TFile } from "obsidian";
import { defaultSettings, SimpleTimeTrackerSettings } from "./settings";
import { SimpleTimeTrackerSettingsTab } from "./settings-tab";
import { displayTracker, Entry, formatDuration, formatTimestamp, getDuration, getRunningEntry, getTotalDuration, isRunning, loadAllTrackers, loadTracker, orderedEntries } from "./tracker";
import {
displayTracker,
Entry,
formatDuration,
formatTimestamp,
getDuration,
getRunningEntry,
getTotalDuration,
isRunning,
loadAllTrackers,
loadTracker,
orderedEntries,
} from "./tracker";
import { TimeTrackingSummary } from "./timeTrackingSummary";

export default class SimpleTimeTrackerPlugin extends Plugin {

public api = {
// verbatim versions of the functions found in tracker.ts with the same parameters
loadTracker, loadAllTrackers, getDuration, getTotalDuration, getRunningEntry, isRunning,
loadTracker,
loadAllTrackers,
getDuration,
getTotalDuration,
getRunningEntry,
isRunning,

// modified versions of the functions found in tracker.ts, with the number of required arguments reduced
formatTimestamp: (timestamp: string) => formatTimestamp(timestamp, this.settings),
formatDuration: (totalTime: number) => formatDuration(totalTime, this.settings),
orderedEntries: (entries: Entry[]) => orderedEntries(entries, this.settings)
formatTimestamp: (timestamp: string) =>
formatTimestamp(timestamp, this.settings),
formatDuration: (totalTime: number) =>
formatDuration(totalTime, this.settings),
orderedEntries: (entries: Entry[]) =>
orderedEntries(entries, this.settings),
};
public settings: SimpleTimeTrackerSettings;

Expand All @@ -21,37 +41,102 @@ export default class SimpleTimeTrackerPlugin extends Plugin {

this.addSettingTab(new SimpleTimeTrackerSettingsTab(this.app, this));

this.registerMarkdownCodeBlockProcessor("simple-time-tracker", (s, e, i) => {
e.empty();
let component = new MarkdownRenderChild(e);
let tracker = loadTracker(s);
this.registerMarkdownCodeBlockProcessor(
"simple-time-tracker",
(s, e, i) => {
e.empty();
let component = new MarkdownRenderChild(e);
let tracker = loadTracker(s);

// Wrap file name in a function since it can change
let filePath = i.sourcePath;
const getFile = () => filePath;
// Wrap file name in a function since it can change
let filePath = i.sourcePath;
const getFile = () => filePath;

// Hook rename events to update the file path
component.registerEvent(this.app.vault.on("rename", (file, oldPath) => {
if (file instanceof TFile && oldPath === filePath) {
filePath = file.path;
}
}));
// Hook rename events to update the file path
component.registerEvent(
this.app.vault.on("rename", (file, oldPath) => {
if (file instanceof TFile && oldPath === filePath) {
filePath = file.path;
}
})
);

displayTracker(tracker, e, getFile, () => i.getSectionInfo(e), this.settings, component);
i.addChild(component);
});
displayTracker(
tracker,
e,
getFile,
() => i.getSectionInfo(e),
this.settings,
component
);
i.addChild(component);
}
);

this.addCommand({
id: `insert`,
name: `Insert Time Tracker`,
editorCallback: (e, _) => {
e.replaceSelection("```simple-time-tracker\n```\n");
}
},
});

this.addCommand({
id: `insert-time-tracking-summary`,
name: `Insert Time Tracking Summary`,
editorCallback: (editor) => this.insertTimeTrackingSummarySummary(editor),
});

this.registerMarkdownCodeBlockProcessor(
"time-tracking-summary",
async (source, el, ctx) => {
const sourceWithoutComments = source
.split("\n")[0]
.replace(/\/\/.*$/g, ''); // Remove everything after //

const params = sourceWithoutComments
.split(",")
.map((s) => s.trim());

const [startDateStr, endDateStr, streamKey] = params;

const timeTracking = new TimeTrackingSummary(
this.app,
this.settings,
this.api
);
await timeTracking.timeTrackingSummaryForPeriod(
el,
startDateStr,
endDateStr,
streamKey // Optional parameter
);
}
);
}


insertTimeTrackingSummarySummary(editor: Editor) {
const now = moment();
// First day of the current month
const firstDay = now.clone().startOf('month').format('YYYY-MM-DD');

// Last day of the current month
const lastDay = now.clone().endOf('month').format('YYYY-MM-DD');

const snippet = `
\`\`\`time-tracking-summary
"${firstDay}", "${lastDay}" // Optional: add ", stream_name" to filter by streams section. By default will print all.
\`\`\``;
editor.replaceSelection(snippet);
}

async loadSettings(): Promise<void> {
this.settings = Object.assign({}, defaultSettings, await this.loadData());
this.settings = Object.assign(
{},
defaultSettings,
await this.loadData()
);
}

async saveSettings(): Promise<void> {
Expand Down
16 changes: 16 additions & 0 deletions src/settings-tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,22 @@ export class SimpleTimeTrackerSettingsTab extends PluginSettingTab {
});
});

new Setting(this.containerEl)
.setName("Tag Configurations")
.setDesc("Configure your tags, icons, and sub-tags using YAML.")
.addTextArea((text) => {
text
.setPlaceholder("Enter tag configurations in YAML format")
.setValue(this.plugin.settings.tagConfigurationsYaml)
.onChange(async (value) => {
this.plugin.settings.tagConfigurationsYaml = value;
await this.plugin.saveSettings();
});
text.inputEl.rows = 15;
text.inputEl.style.width = "100%";
});


this.containerEl.createEl("hr");
this.containerEl.createEl("p", { text: "Need help using the plugin? Feel free to join the Discord server!" });
this.containerEl.createEl("a", { href: "https://link.ellpeck.de/discordweb" }).createEl("img", {
Expand Down
42 changes: 39 additions & 3 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,52 @@ export const defaultSettings: SimpleTimeTrackerSettings = {
csvDelimiter: ",",
fineGrainedDurations: true,
reverseSegmentOrder: false,
timestampDurations: false
timestampDurations: false,
tagConfigurationsYaml: `
# This is a sample configuration file for the tag configurations
# You can have as many 'sections' as you want to track different domains separately or in parallel
# Example section 1
streams:
name: "🌊 Streams"
items:
- topic: "Accounting"
icon: "🧮"
tag: "#tt_accounting"
subTags: []
- topic: "Development"
icon: "💗"
tag: "#tt_dev"
subTags:
- topic: "Frontend"
tag: "#tt_frontend"
subTags: []
- topic: "Backend"
tag: "#tt_backend"
subTags: []
# Example section 2
clients:
name: "👨🏼‍💼 Clients"
items:
- topic: "Client A"
tag: "#tt_client_a"
subTags: []
- topic: "Client B"
tag: "#tt_client_b"
subTags: []`
};

export interface SimpleTimeTrackerSettings {

timestampFormat: string;
editableTimestampFormat: string;
csvDelimiter: string;
fineGrainedDurations: boolean;
reverseSegmentOrder: boolean;
timestampDurations: boolean;

tagConfigurationsYaml: string;
}
Loading

0 comments on commit 40170e4

Please sign in to comment.