Byron / byron

1 stars 2 forks source link

Possible Feedback On Time Tracking CLI Implemented In Rust #4

Open simonsan opened 3 months ago

simonsan commented 3 months ago

Hey Sebastian, fellow Berliner and gitoxide fan here :)

I'm currently working on an activity tracking (and hopefully a future task, project, and time management tool) written in Rust. Some time ago, I was looking at your time sheets and found it impressive to have this level of transparency. For some future self-employment, I wanted to create a CLI activity tracking tool. It's called pace and is on crates.io:

2024-03-08 14_26_14-New Issue · Byron_byron — Mozilla Firefox

2024-03-08 14_26_54-New Issue · Byron_byron — Mozilla Firefox

Currently, it's at the stage, where the base activity tracking and reporting is implemented (although I still need to implement the output to csv, which will come soon. json output of a review summary is already supported).

Some future vision for `pace` in short - implement HTML templating for `review` so people can export their activity logs to their own (and a premade) HTML template and print it as pdf, also they could create bills for customers like that (additional functionality for billable activities would be as easy as adding a new optional struct field to the activity log) - create a `pace-server` after implementing logging to `SQLite` so people could use pace on different devices and essentially self-host - implement task and project management file based first, so pace would create a `projects.pace.toml` in a directory root and then people could create multiple `tasks.pace.toml` in lower directory hierarchies that reference the root `projects.pace.toml` to have hierarchical tasks for e.g. monorepositories - based on the implementation of `tasks` we can then add pomodoro and other workflows - Pomodoro requires a longer running process, so here it would be essential to implement a small GUI like `work-break` did, more like a notification pop-up that is able to handle the pomodoro sessions ![screenshot](https://github.com/Byron/byron/assets/14062932/7f4fd94e-ff2d-4c2c-b257-9bd4910af208) - afterwards it would be nice to expand support from individual use to team collaboration

I wonder if you would be able to test pace a bit and give some feedback from your perspective and use cases, so I can improve it. I think that would be really valuable to me. As I wrote it for people like you (and me).

Until then, and thank you for your work on gitoxide, Simon

Byron commented 3 months ago

Thanks for reaching out, and I am flattered you are also watching gitoxide coming along :).

pace makes a great first impression, both GitHub and CLI help and it's clear it has received a lot of work, thought and love. Currently I use Clockify but I am not happy at all, and it's the reason I also planned to eventually write a time-tracking tool that does it all better :).

pace seems like a project I could rather contribute to, and it's definitely in my mind for when I finally could start such a task. It's probably just 2 to 3 years away 😅.

Unfortunately, I can't really test it out yet for a variety of reasons which I jolt down below. Please do know though that I am very interested, it just seems too early for me.

I also use it from my phone from time to time, but it would be acceptable to only use it on one machine at first. Having it sync across machines is a feature I'd pay for, along with a way to interact with data on a website. Self-hosting would of course be optimal, but such a tool if done right could be a product. Look at Clockify, and that's the 'best' I could find. They are so bad and steal my data, telemetry can't be opted out either. It's insane, to the point where I intentionally obfuscate messages because my time-tracker can't be trusted.

Let's leave this issue open, maybe you can ping me here once my issues above are somewhat addressed so I can try to switch 🙏. Please do keep going :).

simonsan commented 3 months ago

Hey @Byron thanks for your early feedback! <3

  • I would like to learn front and center on the project page how it stores (and keeps safe) my data. My timetracking data is important to me as it includes billable items, and loosing them is like loosing money. Seeing anything less than a real database (like SQLite) as storage backend wouldn't be good enough for me.

Yeah, I understand that, currently it uses a toml file as a storage, because for the beginning I found it easy to use and it could be edited in a text editor, which I found quite important as well, for some use cases. It is locally saved, can be backed up and archived, by e.g. renaming the file. Currently the standard name is activities.pace.toml but I will implement an archiving functionality in the future.

Currently an entry in the log file looks like this:

[01HQXSQ6J231Q50MDPKGQ83CV9]
category = "Developer::Pace"
description = "Explain Piratricia pace"
begin = "2024-03-01T23:59:59"
end = "2024-03-02T16:07:55"
duration = 58076
kind = "activity"
tags = ["dev", "bla"]
status = "ended"

[01HQZRMCXDQZXDKTZSAFKW1JN0]
category = "Developer::Pace"
description = "Explain Piratricia pace"
begin = "2024-03-02T15:21:04"
end = "2024-03-02T16:06:52"
duration = 2748
kind = "intermission"
parent-id = "01HQXSQ6J231Q50MDPKGQ83CV9"
status = "ended"

I already chose a format that will make it easy to adopt the whole infrastructure to a database. As the activity data are already being parsed into a BTreeMap<ActivityGuid, Activity>. The TomlFileStorage is backed by an InMemoryStorage, both implement the Storage trait that will also be implemented for SQLiteStorage. The advantage with the backing in Memory is, that I implemented the tests for InMemoryStorage because that is what TomlFileStorage essentially forwards all calls to.

  • It should be easy to import existing data to be able to start evaluating with a backlog of real-world data. It's fine to support a pre-defined format and the user has to convert to the format themselves at first - alpha-users can do that, even though later one probably wants to implement importers for different sources to make this painless (i.e. pace import -t clockify times.csv).

Importing existing data is one thing, that is on the to-do list, for sure. Do you happen to have some recommendations, which formats you are having in mind? I'm thinking of abstracting that away behind an Importer trait and people can also implement their own importers for the future, because importing is a never-ending task, I think? What would help you the most?

  • It must be possible to export every bit of data. Simply having a SQLite database as backend would qualify already. No user should be locked-in, so this information should probably also be front-and-center on the project page.

Having a toml file, currently, fulfils this, I would say. That being said, it's super helpful, that you state what you want to read on the front page, because the current readme is the one I started out with to track currently implemented features and commands. So I will for sure adapt that in the near future, when I overwork the documentation for users and devs.

  • The HTML templating engine to generate reports or summaries for predefined time ranges is a must for me, as I use this in my invoices.

Absolutely agreed 👍, it's definitely something I will implement rather sooner than later, because I see the need for that as well.

  • Last but not least: tests, without tests I don't trust it's correct. It can't mess up the timesheets or invoices won't be correct. A glimpse at CI didn't reveal any cargo test or I didn't find it. I'd expect journey tests to protect what users everyday works, and lots of unit tests for the 'deal with time segments' part of the program. I also switch between timezones from time to time, that should not lead to incorrect behaviour.

Also agreed, I implemented nearly 100 tests to this day. Especially for dealing with the PaceDateTime, PaceDuration and PaceDate/PaceTime structs internally. I think you may have checked another CI run for docs, which doesn't run the full pipeline to save our planet's resources. The whole test run could be found here: https://github.com/pace-rs/pace/actions/runs/8198816654/job/22422935936#step:6:1

The current testing strategy involves unit tests for a lot of things, integration tests for the main services (ActivityTracker and ActivityStore backed by the InMemoryStorage), snapshot tests for CLI related output. An example for a journey test is test_important_pace_flow_for_activities_passes.

The current test coverage is: 49.03% coverage, 480/979 lines covered (by cargo tarpaulin), where I tried to choose the most significant one's for now, but I will definitely raise that more, to make sure that also edge cases are covered and not just the main functionality.

For timezone switching, I will certainly have to still implement things, currently I only work with chrono::Local. So I will put that on the to-do list.

Byron commented 3 months ago

Hey @Byron thanks for your early feedback! <3

I am glad I could help a little.

  • I would like to learn front and center on the project page how it stores (and keeps safe) my data. My timetracking data is important to me as it includes billable items, and loosing them is like loosing money. Seeing anything less than a real database (like SQLite) as storage backend wouldn't be good enough for me.

Yeah, I understand that, currently it uses a toml file as a storage, because for the beginning I found it easy to use and it could be edited in a text editor, which I found quite important as well, for some use cases. It is locally saved, can be backed up and archived, by e.g. renaming the file. Currently the standard name is activities.pace.toml but I will implement an archiving functionality in the future.

This looks like an append-only file format which has advantages, as at worst it could write garbage to the end of the file leaving it un-parseable. But that can be fixed by hand so it's not a total loss. Concurrency also isn't a problem with typical user interaction as these appends would be so fast it's hard to interleave them with multiple processes/concurrent writes.

Currently an entry in the log file looks like this:

[01HQXSQ6J231Q50MDPKGQ83CV9]
category = "Developer::Pace"
description = "Explain Piratricia pace"
begin = "2024-03-01T23:59:59"
end = "2024-03-02T16:07:55"
duration = 58076
kind = "activity"
tags = ["dev", "bla"]
status = "ended"

[01HQZRMCXDQZXDKTZSAFKW1JN0]
category = "Developer::Pace"
description = "Explain Piratricia pace"
begin = "2024-03-02T15:21:04"
end = "2024-03-02T16:06:52"
duration = 2748
kind = "intermission"
parent-id = "01HQXSQ6J231Q50MDPKGQ83CV9"
status = "ended"

I already chose a format that will make it easy to adopt the whole infrastructure to a database. As the activity data are already being parsed into a BTreeMap<ActivityGuid, Activity>. The TomlFileStorage is backed by an InMemoryStorage, both implement the Storage trait that will also be implemented for SQLiteStorage. The advantage with the backing in Memory is, that I implemented the tests for InMemoryStorage because that is what TomlFileStorage essentially forwards all calls to.

I truly think that using SQLite here would help, for one as rock-solid data store and for users to feel they have control over their data by default. Tools exist to introspect SQLite database, for example, it's easy to export to other formats as well. It's a whole ecosystem that then comes for free.

  • It should be easy to import existing data to be able to start evaluating with a backlog of real-world data. It's fine to support a pre-defined format and the user has to convert to the format themselves at first - alpha-users can do that, even though later one probably wants to implement importers for different sources to make this painless (i.e. pace import -t clockify times.csv).

Importing existing data is one thing, that is on the to-do list, for sure. Do you happen to have some recommendations, which formats you are having in mind? I'm thinking of abstracting that away behind an Importer trait and people can also implement their own importers for the future, because importing is a never-ending task, I think? What would help you the most?

I think having a Rust API for it would be great! I can imagine contributing an implementation of such 'import module' for clockify then, should be easy enough.

  • It must be possible to export every bit of data. Simply having a SQLite database as backend would qualify already. No user should be locked-in, so this information should probably also be front-and-center on the project page.

Having a toml file, currently, fulfils this, I would say. That being said, it's super helpful, that you state what you want to read on the front page, because the current readme is the one I started out with to track currently implemented features and commands. So I will for sure adapt that in the near future, when I overwork the documentation for users and devs.

Yes, I agree, a TOML file would qualify.

  • The HTML templating engine to generate reports or summaries for predefined time ranges is a must for me, as I use this in my invoices.

Absolutely agreed 👍, it's definitely something I will implement rather sooner than later, because I see the need for that as well.

  • Last but not least: tests, without tests I don't trust it's correct. It can't mess up the timesheets or invoices won't be correct. A glimpse at CI didn't reveal any cargo test or I didn't find it. I'd expect journey tests to protect what users everyday works, and lots of unit tests for the 'deal with time segments' part of the program. I also switch between timezones from time to time, that should not lead to incorrect behaviour.

Also agreed, I implemented nearly 100 tests to this day. Especially for dealing with the PaceDateTime, PaceDuration and PaceDate/PaceTime structs internally. I think you may have checked another CI run for docs, which doesn't run the full pipeline to save our planet's resources. The whole test run could be found here: https://github.com/pace-rs/pace/actions/runs/8198816654/job/22422935936#step:6:1

The current testing strategy involves unit tests for a lot of things, integration tests for the main services (ActivityTracker and ActivityStore backed by the InMemoryStorage), snapshot tests for CLI related output. An example for a journey test is test_important_pace_flow_for_activities_passes.

It's great to hear I was just missing them (as in: didn't find them).

The current test coverage is: 49.03% coverage, 480/979 lines covered (by cargo tarpaulin), where I tried to choose the most significant one's for now, but I will definitely raise that more, to make sure that also edge cases are covered and not just the main functionality.

It's really interesting that you use code-coverage in Rust. It's something I don't do at all, but rather, depending on the task admittedly, write tests first. In other cases, higher up the abstraction level, it's usually just one or two tests as fence-poles for typical usage, knowing the the Rust type system probably has you covered. In any case and from the sound of it, you have tests at different levels and that's already what I needed :).

For timezone switching, I will certainly have to still implement things, currently I only work with chrono::Local. So I will put that on the to-do list.

❤️🙏